|
@@ -1,224 +0,0 @@
|
|
-use std::cell::RefCell;
|
|
|
|
-use std::rc::Rc;
|
|
|
|
-
|
|
|
|
-use patina::prelude::*;
|
|
|
|
-use patina::geom::*;
|
|
|
|
-use patina::platform as pp;
|
|
|
|
-
|
|
|
|
-/// ABGR32 surface (assumes little-endian CPU)
|
|
|
|
-///
|
|
|
|
-/// Colourmasks:
|
|
|
|
-/// - 0x000000ff: alpha
|
|
|
|
-/// - 0x0000ff00: blue
|
|
|
|
-/// - 0x00ff0000: green
|
|
|
|
-/// - 0xff000000: red
|
|
|
|
-pub struct UnaccelSurface {
|
|
|
|
- data: Vec<u32>,
|
|
|
|
- width: usize,
|
|
|
|
- height: usize,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl UnaccelSurface {
|
|
|
|
- pub fn new(width: usize, height: usize) -> Self {
|
|
|
|
- let mut data = vec![];
|
|
|
|
- data.resize(width * height, 0);
|
|
|
|
- Self {
|
|
|
|
- width,
|
|
|
|
- height,
|
|
|
|
- data,
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
|
|
|
|
- self.data[x + y * self.width]
|
|
|
|
- }
|
|
|
|
- pub fn set_pixel(&mut self, x: usize, y: usize, val: u32) {
|
|
|
|
- self.data[x + y * self.width] = val;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- pub fn raw_argb32_data(&self) -> &[u8] {
|
|
|
|
- let buf = self.data.as_slice().as_ptr();
|
|
|
|
- let buflen = self.data.len() * 4;
|
|
|
|
- unsafe { std::slice::from_raw_parts(buf as *const u8, buflen) }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl pp::render::Surface for UnaccelSurface {
|
|
|
|
- fn size(&self) -> IVector {
|
|
|
|
- IVector::new(self.width as i32, self.height as i32)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- type Painter<'l> = UnaccelPainter<'l>;
|
|
|
|
- fn painter(&mut self) -> Option<Self::Painter<'_>> {
|
|
|
|
- Some(UnaccelPainter { surf: self })
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-pub struct UnaccelPainter<'l> {
|
|
|
|
- surf: &'l mut UnaccelSurface,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl<'l> pp::render::Painter for UnaccelPainter<'l> {
|
|
|
|
- type Surface = UnaccelSurface;
|
|
|
|
-
|
|
|
|
- fn blit(&mut self, source: &Self::Surface, source_area: IRect, target: IPoint) {
|
|
|
|
- for y in 0..source_area.height() {
|
|
|
|
- for x in 0..source_area.width() {
|
|
|
|
- let data = source.get_pixel(
|
|
|
|
- (source_area.left() + x) as usize,
|
|
|
|
- (source_area.top() + y) as usize,
|
|
|
|
- );
|
|
|
|
- let tx = x + target.x;
|
|
|
|
- let ty = y + target.y;
|
|
|
|
- if tx < 0 || ty < 0 || tx >= self.surf.width as i32 || ty >= self.surf.height as i32
|
|
|
|
- {
|
|
|
|
- continue;
|
|
|
|
- } else {
|
|
|
|
- self.surf.set_pixel(tx as usize, ty as usize, data);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fn fill(&mut self, area: IRect, col: pp::render::Colour) {
|
|
|
|
- let rgba = col.to_rgba();
|
|
|
|
- for yoff in 0..area.height() {
|
|
|
|
- for xoff in 0..area.width() {
|
|
|
|
- self.surf.set_pixel(
|
|
|
|
- (xoff + area.left()) as usize,
|
|
|
|
- (yoff + area.top()) as usize,
|
|
|
|
- rgba,
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- fn outline_rect(&mut self, rect: IRect, col: pp::render::Colour) {
|
|
|
|
- todo!()
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// text rendering via cosmic-text
|
|
|
|
-pub struct UnaccelText {
|
|
|
|
- font: Rc<schrift_rs::Font>,
|
|
|
|
- cached: RefCell<schrift_rs::CachedSchrift>,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl UnaccelText {
|
|
|
|
- pub fn new() -> Self {
|
|
|
|
- let font =
|
|
|
|
- schrift_rs::Font::load_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf")
|
|
|
|
- .unwrap();
|
|
|
|
- let cached = schrift_rs::CachedSchrift::new(
|
|
|
|
- schrift_rs::Schrift::build(font.clone())
|
|
|
|
- .with_scale(16.0)
|
|
|
|
- .flag_y_downwards()
|
|
|
|
- .build(),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- Self {
|
|
|
|
- font,
|
|
|
|
- cached: cached.into(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl pp::TextInterface for UnaccelText {
|
|
|
|
- type Surface = UnaccelSurface;
|
|
|
|
-
|
|
|
|
- fn render_text<'l>(&'l self, what: &pp::text::TextBuffer, sz: f32) -> Self::Surface {
|
|
|
|
- let line = what.line_iter().next().unwrap();
|
|
|
|
-
|
|
|
|
- let mut sch = self.cached.borrow_mut();
|
|
|
|
-
|
|
|
|
- let lm = sch.line_metrics();
|
|
|
|
- let ascender_offset = lm.ascender;
|
|
|
|
- let descender_offset = lm.descender;
|
|
|
|
-
|
|
|
|
- // two passes: first, compute the needed width
|
|
|
|
- let mut cumulative_width = 0;
|
|
|
|
- // let mut max_height = 0;
|
|
|
|
- let glyphs = line
|
|
|
|
- .chars()
|
|
|
|
- .map(|v| sch.glyph_lookup(v))
|
|
|
|
- .collect::<Vec<_>>();
|
|
|
|
- for glyph in glyphs.iter() {
|
|
|
|
- let gm = sch.glyph_metrics(*glyph);
|
|
|
|
- cumulative_width += gm.advance.round() as usize;
|
|
|
|
- }
|
|
|
|
- // add one pixel of padding
|
|
|
|
- cumulative_width += 1;
|
|
|
|
- // use ascender and descender sizes for image height, with one pixel of padding
|
|
|
|
- let height = (ascender_offset.abs().round() + descender_offset.abs().round() + 1.0) as usize;
|
|
|
|
-
|
|
|
|
- let mut surf = UnaccelSurface::new(cumulative_width, height);
|
|
|
|
-
|
|
|
|
- // second pass: copy the glyph images over
|
|
|
|
- let mut cursor = 0;
|
|
|
|
- let mut last_glyph = None;
|
|
|
|
- for glyph in glyphs.iter() {
|
|
|
|
- let kerning_offset = match last_glyph.take() {
|
|
|
|
- Some(lglyph) => {
|
|
|
|
- let gk = sch.glyph_kerning(lglyph, *glyph);
|
|
|
|
- (gk.x_shift, gk.y_shift)
|
|
|
|
- }
|
|
|
|
- None => {
|
|
|
|
- (0., 0.)
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- let (left_bearing, advance, y_offset) = {
|
|
|
|
- let gm = sch.glyph_metrics(*glyph);
|
|
|
|
- (gm.left_bearing, gm.advance, gm.y_offset + (kerning_offset.1 + ascender_offset).round() as i32)
|
|
|
|
- };
|
|
|
|
- let img = sch.glyph_image(*glyph);
|
|
|
|
- let x_offset = (left_bearing + kerning_offset.0).round() as i32;
|
|
|
|
- for x in 0..img.1 {
|
|
|
|
- for y in 0..img.2 {
|
|
|
|
- let intensity = img.0[(y * img.1 + x) as usize] as u32;
|
|
|
|
- surf.set_pixel(
|
|
|
|
- ((cursor + x).checked_add_signed(x_offset).unwrap()) as usize,
|
|
|
|
- (y as i32 + y_offset) as usize,
|
|
|
|
- // 0xffffff00 | intensity,
|
|
|
|
- intensity
|
|
|
|
- // 0x00ffffff | (intensity << 24)
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- cursor += advance.round() as u32;
|
|
|
|
- last_glyph = Some(*glyph);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- surf
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#[cfg(test)]
|
|
|
|
-#[test]
|
|
|
|
-fn test_unaccel_text() {
|
|
|
|
- use pp::render::Surface;
|
|
|
|
- use pp::TextInterface;
|
|
|
|
- let ut = UnaccelText::new();
|
|
|
|
-
|
|
|
|
- let tb = pp::text::TextBuffer::new("unaccelerated text rendering ahoy");
|
|
|
|
-
|
|
|
|
- let surf = ut.render_text(&tb, 16.0);
|
|
|
|
- println!("{:?}", surf.size());
|
|
|
|
-
|
|
|
|
- let mut pgm = String::new();
|
|
|
|
- pgm.push_str("P2\n");
|
|
|
|
- pgm.push_str(format!("{} {}\n", surf.size().x, surf.size().y).as_str());
|
|
|
|
- pgm.push_str("255\n");
|
|
|
|
- for y in 0..surf.size().y {
|
|
|
|
- for x in 0..surf.size().x {
|
|
|
|
- pgm.push_str(format!(" {}", surf.get_pixel(x as usize, y as usize) & 0xff).as_str());
|
|
|
|
- }
|
|
|
|
- pgm.push('\n');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::fs::write("render_output.pgm", pgm).unwrap();
|
|
|
|
-
|
|
|
|
- // surf
|
|
|
|
-
|
|
|
|
- // ductr::AnymapImage::ppm(data, 255, height, width);
|
|
|
|
-
|
|
|
|
- // let rendered = write_as_ascii("unaccelerated.ppm").unwrap();
|
|
|
|
-}
|
|
|