darkfi 2 лет назад
Родитель
Сommit
60858f36d6
3 измененных файлов с 175 добавлено и 98 удалено
  1. 9 2
      bin/darkwallet/Cargo.toml
  2. 2 4
      bin/darkwallet/gui/__init__.py
  3. 164 92
      bin/darkwallet/src/gfx.rs

+ 9 - 2
bin/darkwallet/Cargo.toml

@@ -10,9 +10,10 @@ edition = "2021"
 [dependencies]
 #miniquad = "0.4.0-alpha"
 miniquad = { git = "https://github.com/not-fl3/miniquad" }
-#harfbuzz_rs = "2.0.1"
 harfbuzz_rs = { git = "https://github.com/narodnik/harfbuzz_rs.git", features = ["freetype"] }
-freetype-rs = "0.36.0"
+freetype-rs = { git = "https://github.com/narodnik/freetype-rs.git" }
+#harfbuzz_rs = "2.0.1"
+#freetype-rs = "0.36.0"
 freetype-sys = "0.20.1"
 image = "0.24"
 log = "0.4.19"
@@ -26,6 +27,12 @@ atomic_float = "0.1.0"
 
 rustpython-vm = "0.3.1"
 
+#[patch."https://github.com/narodnik/harfbuzz_rs.git"]
+#freetype-rs = { path = "/tmp/freetype-rs/" }
+
+[patch.crates-io]
+freetype-rs = { git = "https://github.com/narodnik/freetype-rs.git" }
+
 [target.'cfg(target_os = "android")'.dependencies]
 android_logger = "0.13"
 

+ 2 - 4
bin/darkwallet/gui/__init__.py

@@ -441,7 +441,7 @@ def draw():
         False, True, 1, 0.0, None, []
     )
     api.add_property(node_id, prop)
-    api.set_property_f32(node_id, "font_size", 0, 80)
+    api.set_property_f32(node_id, "font_size", 0, 30)
 
     prop = Property(
         "text", PropertyType.STR, PropertySubType.NULL,
@@ -450,9 +450,7 @@ def draw():
         False, False, 1, None, None, []
     )
     api.add_property(node_id, prop)
-    api.set_property_str(node_id, "text", 0, "hihi 😁")
-    #api.set_property_str(node_id, "text", 0, "hello! jelly 🍆")
-    #api.set_property_str(node_id, "text", 0, "hello! jelly")
+    api.set_property_str(node_id, "text", 0, "hello!😁jelly 🍆1234")
 
     prop = Property(
         "color", PropertyType.FLOAT32, PropertySubType.COLOR,

+ 164 - 92
bin/darkwallet/src/gfx.rs

@@ -107,7 +107,7 @@ struct Stage<'a> {
     scene_graph: SceneGraphPtr,
 
     textures: ResourceManager<TextureId>,
-    ft_face: ft::Face<&'a [u8]>,
+    font_faces: Vec<ft::Face<&'a [u8]>>,
 
     method_recvr: mpsc::Receiver<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
     method_sender: mpsc::SyncSender<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
@@ -165,17 +165,23 @@ impl<'a> Stage<'a> {
 
         let (method_sender, method_recvr) = mpsc::sync_channel(100);
 
-        let font_data = include_bytes!("../Inter-Regular.otf") as &[u8];
-        //let font_data = include_bytes!("../NotoColorEmoji.ttf") as &[u8];
         let ftlib = ft::Library::init().unwrap();
+        let mut font_faces = vec![];
+
+        let font_data = include_bytes!("../Inter-Regular.otf") as &[u8];
+        let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
+        font_faces.push(ft_face);
+
+        let font_data = include_bytes!("../NotoColorEmoji.ttf") as &[u8];
         let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
+        font_faces.push(ft_face);
 
         let mut stage = Stage {
             ctx,
             pipeline,
             scene_graph,
             textures,
-            ft_face,
+            font_faces,
             method_recvr,
             method_sender,
             last_draw_time: None,
@@ -338,7 +344,7 @@ struct RenderContext<'a> {
     pipeline: &'a Pipeline,
     proj: glam::Mat4,
     textures: &'a ResourceManager<TextureId>,
-    ft_face: &'a ft::Face<&'a [u8]>,
+    font_faces: &'a Vec<ft::Face<&'a [u8]>>,
 }
 
 impl<'a> RenderContext<'a> {
@@ -648,107 +654,173 @@ impl<'a> RenderContext<'a> {
 
         self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
 
-        self.ft_face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap();
-        // emojis required a fixed size
-        //self.ft_face.set_char_size(109 * 64, 0, 72, 72).unwrap();
+        //let mut strings = vec![];
+        //let mut current_str = String::new();
+        //let mut current_idx = 0;
+        //for chr in text.chars() {
+        //    let ft_face = self.font_faces[current_idx];
+        //    if ft_face.get_char_index(chr as usize).is_some() {
+        //    }
+        //}
+
+        let mut current_idx = 0;
+        let mut current_str = String::new();
+        let mut substrs = vec![];
+        'next_char: for chr in text.chars() {
+            let idx = 'get_idx: {
+                for i in 0..self.font_faces.len() {
+                    let ft_face = &self.font_faces[i];
+                    if ft_face.get_char_index(chr as usize).is_some() {
+                        break 'get_idx i
+                    }
+                }
 
-        let hb_font = harfbuzz_rs::Font::from_freetype_face(self.ft_face.clone());
-        let buffer = harfbuzz_rs::UnicodeBuffer::new().add_str(&text);
-        let output = harfbuzz_rs::shape(&hb_font, buffer, &[]);
+                warn!("no font fallback for char: {}", chr);
+                // Skip this char
+                continue 'next_char
+            };
+            if current_idx != idx {
+                if !current_str.is_empty() {
+                    // Push
+                    substrs.push((current_idx, current_str.clone()));
+                }
 
-        let positions = output.get_glyph_positions();
-        let infos = output.get_glyph_infos();
+                current_str.clear();
+                current_idx = idx;
+            }
+            current_str.push(chr);
+        }
+        if !current_str.is_empty() {
+            // Push
+            substrs.push((current_idx, current_str));
+        }
 
         let mut current_x = 0.;
         let mut current_y = 0.;
-        for (position, info) in positions.iter().zip(infos) {
-            let gid = info.codepoint;
-
-            self.ft_face.load_glyph(gid, ft::face::LoadFlag::COLOR).unwrap();
-
-            let glyph = self.ft_face.glyph();
-            glyph.render_glyph(ft::RenderMode::Normal).unwrap();
-
-            // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
-
-            let bmp = glyph.bitmap();
-            let buffer = bmp.buffer();
-            let width = bmp.width();
-            let height = bmp.rows();
-            let bearing_x = glyph.bitmap_left() as f32;
-            let bearing_y = glyph.bitmap_top() as f32;
-
-            //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Bgra);
-            //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Lcd);
-            //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Gray);
-
-            let pixel_mode = bmp.pixel_mode().unwrap();
-            let tdata = match pixel_mode {
-                ft::bitmap::PixelMode::Bgra => {
-                    let mut tdata = vec![];
-                    tdata.resize((4 * width * height) as usize, 0);
-                    // Convert from BGRA to RGBA
-                    for i in 0..(width*height) as usize {
-                        let idx = i*4;
-                        let b = buffer[idx];
-                        let g = buffer[idx + 1];
-                        let r = buffer[idx + 2];
-                        let a = buffer[idx + 3];
-                        tdata[idx] = r;
-                        tdata[idx + 1] = g;
-                        tdata[idx + 2] = b;
-                        tdata[idx + 3] = a;
-                    }
-                    tdata
-                }
-                ft::bitmap::PixelMode::Gray => {
-                    // Convert from greyscale to RGBA8
-                    let tdata: Vec<_> = buffer
-                        .iter()
-                        .flat_map(|coverage| {
-                            let r = (255. * color_r) as u8;
-                            let g = (255. * color_g) as u8;
-                            let b = (255. * color_b) as u8;
-                            let α = ((*coverage as f32) * color_a) as u8;
-                            vec![r, g, b, α]
-                        })
-                        .collect();
-                    tdata
+
+        for (face_idx, text) in substrs {
+            let face = &self.font_faces[face_idx];
+            if face.has_fixed_sizes() {
+                // emojis required a fixed size
+                //face.set_char_size(109 * 64, 0, 72, 72).unwrap();
+                face.select_size(0).unwrap();
+            } else {
+                face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap();
+            }
+
+            let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone());
+            let buffer = harfbuzz_rs::UnicodeBuffer::new().add_str(&text);
+            let output = harfbuzz_rs::shape(&hb_font, buffer, &[]);
+
+            let positions = output.get_glyph_positions();
+            let infos = output.get_glyph_infos();
+
+            for (position, info) in positions.iter().zip(infos) {
+                let gid = info.codepoint;
+
+                let mut flags = ft::face::LoadFlag::DEFAULT;
+                if face.has_color() {
+                    flags |= ft::face::LoadFlag::COLOR;
                 }
-                _ => panic!("unsupport pixel mode: {:?}", pixel_mode)
-            };
+                face.load_glyph(gid, flags).unwrap();
+
+                let glyph = face.glyph();
+                glyph.render_glyph(ft::RenderMode::Normal).unwrap();
+
+                // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
+
+                let bmp = glyph.bitmap();
+                let buffer = bmp.buffer();
+                let bmp_width = bmp.width() as usize;
+                let bmp_height = bmp.rows() as usize;
+                let bearing_x = glyph.bitmap_left() as f32;
+                let bearing_y = glyph.bitmap_top() as f32;
+
+                //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Bgra);
+                //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Lcd);
+                //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Gray);
+
+                let pixel_mode = bmp.pixel_mode().unwrap();
+                let tdata = match pixel_mode {
+                    ft::bitmap::PixelMode::Bgra => {
+                        let mut tdata = vec![];
+                        tdata.resize(4 * bmp_width * bmp_height, 0);
+                        // Convert from BGRA to RGBA
+                        for i in 0..bmp_width*bmp_height as usize {
+                            let idx = i*4;
+                            let b = buffer[idx];
+                            let g = buffer[idx + 1];
+                            let r = buffer[idx + 2];
+                            let a = buffer[idx + 3];
+                            tdata[idx] = r;
+                            tdata[idx + 1] = g;
+                            tdata[idx + 2] = b;
+                            tdata[idx + 3] = a;
+                        }
+                        tdata
+                    }
+                    ft::bitmap::PixelMode::Gray => {
+                        // Convert from greyscale to RGBA8
+                        let tdata: Vec<_> = buffer
+                            .iter()
+                            .flat_map(|coverage| {
+                                let r = (255. * color_r) as u8;
+                                let g = (255. * color_g) as u8;
+                                let b = (255. * color_b) as u8;
+                                let α = ((*coverage as f32) * color_a) as u8;
+                                vec![r, g, b, α]
+                            })
+                            .collect();
+                        tdata
+                    }
+                    _ => panic!("unsupport pixel mode: {:?}", pixel_mode)
+                };
+
+                let (x1, y1, x2, y2) = if face.has_fixed_sizes() {
+                    // Downscale by height
+                    let width = (bmp_width as f32 * font_size) / bmp_height as f32;
+                    let height = font_size;
 
-            let off_x = position.x_offset as f32 / 64.;
-            let off_y = position.y_offset as f32 / 64.;
+                    let x1 = current_x;
+                    let y1 = current_y - height;
 
-            let x1 = current_x + off_x + bearing_x;
-            let y1 = current_y - off_y - bearing_y;
-            let x2 = x1 + width as f32;
-            let y2 = y1 + height as f32;
+                    let x2 = current_x + width;
+                    let y2 = current_y;
 
-            let x_advance = position.x_advance as f32 / 64.;
-            let y_advance = position.y_advance as f32 / 64.;
-            current_x += x_advance;
-            current_y += y_advance;
+                    current_x += width;
 
-            //println!("(s0, t0) = ({}, {}),  (s1, t1) = ({}, {})", s0, t0, s1, t1);
-            //println!("(xa, ya) = ({}, {}),  (xo, yo) = ({}, {})", x_advance, y_advance, off_x, off_y);
-            //println!("(bx, by) = ({}, {})", bearing_x, bearing_y);
-            //println!("(w, h)   = ({}, {})", width, height);
-            //println!("(x1, y1) = ({}, {}),  (x2, y2) = ({}, {})", x1, y1, x2, y2);
-            //println!();
+                    (x1, y1, x2, y2)
+                } else {
+                    let (width, height) = (bmp_width as f32, bmp_height as f32);
 
-            let texture = self.ctx.new_texture_from_rgba8(width as u16, height as u16, &tdata);
-            self.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture);
-            self.ctx.delete_texture(texture);
+                    let off_x = position.x_offset as f32 / 64.;
+                    let off_y = position.y_offset as f32 / 64.;
 
+                    let x1 = current_x + off_x + bearing_x;
+                    let y1 = current_y - off_y - bearing_y;
+                    let x2 = x1 + width as f32;
+                    let y2 = y1 + height as f32;
+
+                    let x_advance = position.x_advance as f32 / 64.;
+                    let y_advance = position.y_advance as f32 / 64.;
+                    current_x += x_advance;
+                    current_y += y_advance;
+
+                    (x1, y1, x2, y2)
+                };
+
+                let texture = self.ctx.new_texture_from_rgba8(bmp_width as u16, bmp_height as u16, &tdata);
+                self.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture);
+                self.ctx.delete_texture(texture);
+
+                if debug {
+                    self.outline(x1, y1, x2, y2, COLOR_BLUE, 1.);
+                }
+            }
             if debug {
-                self.outline(x1, y1, x2, y2, COLOR_BLUE, 1.);
+                self.hline(0., current_x, 0., COLOR_RED, 1.);
             }
         }
-        if debug {
-            self.hline(0., current_x, 0., COLOR_RED, 1.);
-        }
 
         Ok(())
     }
@@ -809,7 +881,7 @@ impl<'a> EventHandler for Stage<'a> {
             pipeline: &self.pipeline,
             proj,
             textures: &self.textures,
-            ft_face: &self.ft_face,
+            font_faces: &self.font_faces,
         };
 
         render_context.render_window();