Просмотр исходного кода

wallet: working OTF fancy text rendering

darkfi 2 лет назад
Родитель
Сommit
fa9dc5c3f9

+ 4 - 0
bin/darkwallet/Cargo.toml

@@ -11,6 +11,10 @@ edition = "2021"
 #miniquad = "0.4.0-alpha"
 miniquad = { git = "https://github.com/not-fl3/miniquad" }
 fontdue = "0.7"
+#harfbuzz_rs = "2.0.1"
+harfbuzz_rs = { git = "https://github.com/narodnik/harfbuzz_rs.git", features = ["freetype"] }
+freetype-rs = "0.36.0"
+freetype-sys = "0.20.1"
 image = "0.24"
 log = "0.4.19"
 glam = "0.27.0"

BIN
bin/darkwallet/Inter-Regular.otf


BIN
bin/darkwallet/Inter-Regular.ttf


+ 132 - 66
bin/darkwallet/gui/__init__.py

@@ -135,10 +135,10 @@ def reposition_cursor():
     set_property_f32("/window/cursor_layer/cursor", "y", y)
 
 def draw_cursor():
-    mesh_id = api.add_node("cursor_box", SceneNodeType.RENDER_MESH)
-    api.add_property(mesh_id, "verts", PropertyType.BUFFER)
-    api.add_property(mesh_id, "faces", PropertyType.BUFFER)
-    link_node(mesh_id, "/window/cursor_layer/cursor")
+    node_id = api.add_node("cursor_box", SceneNodeType.RENDER_MESH)
+    api.add_property(node_id, "verts", PropertyType.BUFFER)
+    api.add_property(node_id, "faces", PropertyType.BUFFER)
+    link_node(node_id, "/window/cursor_layer/cursor")
 
     x, y = 0, 0
     w, h = 20, 40
@@ -146,24 +146,24 @@ def draw_cursor():
     vert2 = vertex(x + w, y,     1, 1, 1, 1, 1, 0)
     vert3 = vertex(x,     y + h, 1, 1, 1, 1, 0, 1)
     vert4 = vertex(x + w, y + h, 1, 1, 1, 1, 1, 1)
-    api.set_property_buffer(mesh_id, "verts", vert1 + vert2 + vert3 + vert4)
-    api.set_property_buffer(mesh_id, "faces", face(0, 2, 1) + face(1, 2, 3))
+    api.set_property_buffer(node_id, "verts", vert1 + vert2 + vert3 + vert4)
+    api.set_property_buffer(node_id, "faces", face(0, 2, 1) + face(1, 2, 3))
 
 def draw_box():
-    mesh_id = api.add_node("box", SceneNodeType.RENDER_MESH)
-    api.add_property(mesh_id, "verts", PropertyType.BUFFER)
-    api.add_property(mesh_id, "faces", PropertyType.BUFFER)
-    link_node(mesh_id, "/window/chatbox_layer/outline")
+    node_id = api.add_node("box", SceneNodeType.RENDER_MESH)
+    api.add_property(node_id, "verts", PropertyType.BUFFER)
+    api.add_property(node_id, "faces", PropertyType.BUFFER)
+    link_node(node_id, "/window/chatbox_layer/outline")
 
-    mesh_id = api.add_node("inner_box", SceneNodeType.RENDER_MESH)
-    api.add_property(mesh_id, "verts", PropertyType.BUFFER)
-    api.add_property(mesh_id, "faces", PropertyType.BUFFER)
-    link_node(mesh_id, "/window/chatbox_layer/outline")
+    node_id = api.add_node("inner_box", SceneNodeType.RENDER_MESH)
+    api.add_property(node_id, "verts", PropertyType.BUFFER)
+    api.add_property(node_id, "faces", PropertyType.BUFFER)
+    link_node(node_id, "/window/chatbox_layer/outline")
 
     resize_box()
 
 def resize_box():
-    mesh_id = api.lookup_node_id("/window/chatbox_layer/outline/box")
+    node_id = api.lookup_node_id("/window/chatbox_layer/outline/box")
 
     layer_w = get_property("/window/chatbox_layer", "rect_w")
     layer_h = get_property("/window/chatbox_layer", "rect_h")
@@ -179,11 +179,11 @@ def resize_box():
     vert2 = vertex(x + w, y,     1, 1, 1, 1, 1, 0)
     vert3 = vertex(x,     y + h, 1, 1, 1, 1, 0, 1)
     vert4 = vertex(x + w, y + h, 1, 1, 1, 1, 1, 1)
-    api.set_property_buffer(mesh_id, "verts", vert1 + vert2 + vert3 + vert4)
-    api.set_property_buffer(mesh_id, "faces", face(0, 2, 1) + face(1, 2, 3))
+    api.set_property_buffer(node_id, "verts", vert1 + vert2 + vert3 + vert4)
+    api.set_property_buffer(node_id, "faces", face(0, 2, 1) + face(1, 2, 3))
 
     # Second mesh
-    mesh_id = api.lookup_node_id("/window/chatbox_layer/outline/inner_box")
+    node_id = api.lookup_node_id("/window/chatbox_layer/outline/inner_box")
 
     x, y = x + padding, y + padding
     w -= 2*padding
@@ -192,24 +192,24 @@ def resize_box():
     vert2 = vertex(x + w, y,     0, 0, 0, 1, 1, 0)
     vert3 = vertex(x,     y + h, 0, 0, 0, 1, 0, 1)
     vert4 = vertex(x + w, y + h, 0.3, 0.3, 0.3, 1, 1, 1)
-    api.set_property_buffer(mesh_id, "verts", vert1 + vert2 + vert3 + vert4)
-    api.set_property_buffer(mesh_id, "faces", face(0, 2, 1) + face(1, 2, 3))
+    api.set_property_buffer(node_id, "verts", vert1 + vert2 + vert3 + vert4)
+    api.set_property_buffer(node_id, "faces", face(0, 2, 1) + face(1, 2, 3))
 
 def draw_rounded_box():
-    mesh_id = api.add_node("box", SceneNodeType.RENDER_MESH)
-    api.add_property(mesh_id, "verts", PropertyType.BUFFER)
-    api.add_property(mesh_id, "faces", PropertyType.BUFFER)
-    link_node(mesh_id, "/window/rounded_box_layer/box")
+    node_id = api.add_node("box", SceneNodeType.RENDER_MESH)
+    api.add_property(node_id, "verts", PropertyType.BUFFER)
+    api.add_property(node_id, "faces", PropertyType.BUFFER)
+    link_node(node_id, "/window/rounded_box_layer/box")
 
-    mesh_id = api.add_node("inner_box", SceneNodeType.RENDER_MESH)
-    api.add_property(mesh_id, "verts", PropertyType.BUFFER)
-    api.add_property(mesh_id, "faces", PropertyType.BUFFER)
-    link_node(mesh_id, "/window/rounded_box_layer/box")
+    node_id = api.add_node("inner_box", SceneNodeType.RENDER_MESH)
+    api.add_property(node_id, "verts", PropertyType.BUFFER)
+    api.add_property(node_id, "faces", PropertyType.BUFFER)
+    link_node(node_id, "/window/rounded_box_layer/box")
 
     resize_rounded_box()
 
 def resize_rounded_box():
-    mesh_id = api.lookup_node_id("/window/rounded_box_layer/box/box")
+    node_id = api.lookup_node_id("/window/rounded_box_layer/box/box")
 
     layer_w = get_property("/window/rounded_box_layer", "rect_w")
     layer_h = get_property("/window/rounded_box_layer", "rect_h")
@@ -254,11 +254,11 @@ def resize_rounded_box():
     )
     o = 8
     faces += face(o + 0, o + 2, o + 1) + face(o + 1, o + 2, o + 3)
-    api.set_property_buffer(mesh_id, "verts", verts)
-    api.set_property_buffer(mesh_id, "faces", faces)
+    api.set_property_buffer(node_id, "verts", verts)
+    api.set_property_buffer(node_id, "faces", faces)
 
     # Second mesh
-    mesh_id = api.lookup_node_id("/window/rounded_box_layer/box/inner_box")
+    node_id = api.lookup_node_id("/window/rounded_box_layer/box/inner_box")
 
     x, y = x + padding, y + padding
     w -= 2*padding
@@ -267,8 +267,8 @@ def resize_rounded_box():
     vert2 = vertex(x + w, y,     0, 0, 0, 1, 1, 0)
     vert3 = vertex(x,     y + h, 0, 0, 0, 1, 0, 1)
     vert4 = vertex(x + w, y + h, 0.3, 0.3, 0.3, 1, 1, 1)
-    #api.set_property_buffer(mesh_id, "verts", vert1 + vert2 + vert3 + vert4)
-    #api.set_property_buffer(mesh_id, "faces", face(0, 2, 1) + face(1, 2, 3))
+    #api.set_property_buffer(node_id, "verts", vert1 + vert2 + vert3 + vert4)
+    #api.set_property_buffer(node_id, "faces", face(0, 2, 1) + face(1, 2, 3))
 
 def draw():
     win_id = api.lookup_node_id("/window")
@@ -280,7 +280,7 @@ def draw():
     prop = Property(
         "is_visible", PropertyType.BOOL, PropertySubType.NULL,
         None,
-        "is_visible", "Visibility of the layer",
+        "Is Visible", "Visibility of the layer",
         False, False, 1, None, None, []
     )
     api.add_property(layer_id, prop)
@@ -298,7 +298,7 @@ def draw():
     prop = Property(
         "rect", PropertyType.UINT32, PropertySubType.PIXEL,
         None,
-        "mesh_rect", "The position and size within the layer",
+        "Rectangle", "The position and size within the layer",
         False, True, 4, None, None, []
     )
     api.add_property(layer_id, prop)
@@ -317,16 +317,17 @@ def draw():
     api.link_node(layer_id, win_id)
 
     # Add a mesh to our layer
+    """
 
-    mesh_id = api.add_node("meshie", SceneNodeType.RENDER_MESH)
+    node_id = api.add_node("meshie", SceneNodeType.RENDER_MESH)
 
     prop = Property(
         "data", PropertyType.BUFFER, PropertySubType.NULL,
         None,
-        "mesh_data", "The face and vertex data for the mesh",
+        "Mesh Data", "The face and vertex data for the mesh",
         False, False, 2, None, None, []
     )
-    api.add_property(mesh_id, prop)
+    api.add_property(node_id, prop)
 
     #x, y = 0.1, 0.1
     #w, h = 0.1, 0.1
@@ -340,50 +341,50 @@ def draw():
     verts = vert1 + vert2 + vert3 + vert4
     faces = face(0, 2, 1) + face(1, 2, 3)
 
-    api.set_property_buf(mesh_id, "data", 0, verts)
-    api.set_property_buf(mesh_id, "data", 1, faces)
+    api.set_property_buf(node_id, "data", 0, verts)
+    api.set_property_buf(node_id, "data", 1, faces)
 
     prop = Property(
         "rect", PropertyType.FLOAT32, PropertySubType.PIXEL,
         None,
-        "mesh_rect", "The position and size within the layer",
+        "Rectangle", "The position and size within the layer",
         False, True, 4, None, None, []
     )
-    api.add_property(mesh_id, prop)
+    api.add_property(node_id, prop)
     # x
-    api.set_property_f32(mesh_id, "rect", 0, 10)
+    api.set_property_f32(node_id, "rect", 0, 10)
     # y
-    api.set_property_f32(mesh_id, "rect", 1, 10)
+    api.set_property_f32(node_id, "rect", 1, 10)
     # w
-    #api.set_property_f32(mesh_id, "rect", 2, 20)
+    #api.set_property_f32(node_id, "rect", 2, 20)
     code = [["-", ["load", "lw"], ["f32", 10]]]
-    api.set_property_expr(mesh_id, "rect", 2, code)
+    api.set_property_expr(node_id, "rect", 2, code)
     # h
-    #api.set_property_str(mesh_id, "rect", 3, "lh - 10")
+    #api.set_property_str(node_id, "rect", 3, "lh - 10")
     code = [["-", ["load", "lh"], ["f32", 10]]]
-    api.set_property_expr(mesh_id, "rect", 3, code)
+    api.set_property_expr(node_id, "rect", 3, code)
 
     prop = Property(
         "z_index", PropertyType.UINT32, PropertySubType.NULL,
         None,
-        "z-index", "Z-index: values greater than zero are deferred draws",
+        "Z-index", "Z-index: values greater than zero are deferred draws",
         False, False, 1, None, None, []
     )
-    api.add_property(mesh_id, prop)
+    api.add_property(node_id, prop)
 
-    api.link_node(mesh_id, layer_id)
+    api.link_node(node_id, layer_id)
 
     # Add a second mesh to our layer
 
-    mesh_id = api.add_node("meshie2", SceneNodeType.RENDER_MESH)
+    node_id = api.add_node("meshie2", SceneNodeType.RENDER_MESH)
 
     prop = Property(
         "data", PropertyType.BUFFER, PropertySubType.NULL,
         None,
-        "mesh_data", "The face and vertex data for the mesh",
+        "Mesh Data", "The face and vertex data for the mesh",
         False, False, 2, None, None, []
     )
-    api.add_property(mesh_id, prop)
+    api.add_property(node_id, prop)
 
     x, y, w, h = 0, 0, 1, 1
     vert1 = vertex(x,     y,     1, 0, 1, 1, 0, 0)
@@ -394,30 +395,95 @@ def draw():
     verts = vert1 + vert2 + vert3 + vert4
     faces = face(0, 2, 1) + face(1, 2, 3)
 
-    api.set_property_buf(mesh_id, "data", 0, verts)
-    api.set_property_buf(mesh_id, "data", 1, faces)
+    api.set_property_buf(node_id, "data", 0, verts)
+    api.set_property_buf(node_id, "data", 1, faces)
 
     prop = Property(
         "rect", PropertyType.FLOAT32, PropertySubType.PIXEL,
         None,
-        "mesh_rect", "The position and size within the layer",
+        "Rectangle", "The position and size within the layer",
         False, True, 4, None, None, []
     )
-    api.add_property(mesh_id, prop)
-    api.set_property_f32(mesh_id, "rect", 0, 10)
-    api.set_property_f32(mesh_id, "rect", 1, 10)
-    api.set_property_f32(mesh_id, "rect", 2, 60)
-    api.set_property_f32(mesh_id, "rect", 3, 60)
+    api.add_property(node_id, prop)
+    api.set_property_f32(node_id, "rect", 0, 10)
+    api.set_property_f32(node_id, "rect", 1, 10)
+    api.set_property_f32(node_id, "rect", 2, 60)
+    api.set_property_f32(node_id, "rect", 3, 60)
 
     prop = Property(
         "z_index", PropertyType.UINT32, PropertySubType.NULL,
         None,
-        "z-index", "Z-index: values greater than zero are deferred draws",
+        "Z-index", "Z-index: values greater than zero are deferred draws",
         False, False, 1, None, None, []
     )
-    api.add_property(mesh_id, prop)
+    api.add_property(node_id, prop)
 
-    api.link_node(mesh_id, layer_id)
+    api.link_node(node_id, layer_id)
+    """
+
+    # Add some text
+
+    node_id = api.add_node("hellowurld", SceneNodeType.RENDER_TEXT)
+
+    prop = Property(
+        "rect", PropertyType.FLOAT32, PropertySubType.PIXEL,
+        None,
+        "Rectangle", "The position and size within the layer",
+        False, True, 4, None, None, []
+    )
+    api.add_property(node_id, prop)
+    api.set_property_f32(node_id, "rect", 0, 10)
+    api.set_property_f32(node_id, "rect", 1, 100)
+    api.set_property_f32(node_id, "rect", 2, 60)
+    api.set_property_f32(node_id, "rect", 3, 60)
+
+    prop = Property(
+        "font_size", PropertyType.FLOAT32, PropertySubType.PIXEL,
+        None,
+        "Font Size", "Font Size",
+        False, True, 1, 0.0, None, []
+    )
+    api.add_property(node_id, prop)
+    api.set_property_f32(node_id, "font_size", 0, 80)
+
+    prop = Property(
+        "text", PropertyType.STR, PropertySubType.NULL,
+        None,
+        "Text", "Text",
+        False, False, 1, None, None, []
+    )
+    api.add_property(node_id, prop)
+    api.set_property_str(node_id, "text", 0, "hello! jelly")
+
+    prop = Property(
+        "overflow", PropertyType.ENUM, PropertySubType.NULL,
+        None,
+        "Overflow Behaviour", "Behaviour when text exceeds bounding box",
+        False, True, 1, None, None, [
+            "ScrollRight",
+            "OverflowRight"
+        ]
+    )
+    api.add_property(node_id, prop)
+    api.set_property_enum(node_id, "overflow", 0, "ScrollRight")
+
+    prop = Property(
+        "z_index", PropertyType.UINT32, PropertySubType.NULL,
+        None,
+        "Z-index", "Z-index: values greater than zero are deferred draws",
+        False, False, 1, None, None, []
+    )
+    api.add_property(node_id, prop)
+
+    prop = Property(
+        "debug", PropertyType.BOOL, PropertySubType.NULL,
+        None,
+        "Debug", "Draw debug outlines",
+        False, False, 1, None, None, []
+    )
+    api.add_property(node_id, prop)
+
+    api.link_node(node_id, layer_id)
 
 def main():
     draw()

+ 9 - 0
bin/darkwallet/pydrk/api.py

@@ -576,6 +576,15 @@ class Api:
         serial.encode_str(req, val)
         self._make_request(Command.SET_PROPERTY_VALUE, req)
 
+    def set_property_enum(self, node_id, prop_name, i, val):
+        req = bytearray()
+        serial.write_u32(req, node_id)
+        serial.encode_str(req, prop_name)
+        serial.write_u32(req, i)
+        serial.write_u8(req, PropertyType.ENUM)
+        serial.encode_str(req, val)
+        self._make_request(Command.SET_PROPERTY_VALUE, req)
+
     def set_property_buf(self, node_id, prop_name, i, buf):
         req = bytearray()
         serial.write_u32(req, node_id)

+ 451 - 11
bin/darkwallet/src/gfx.rs

@@ -3,7 +3,37 @@ use fontdue::{
     layout::{CoordinateSystem, GlyphPosition, Layout, LayoutSettings, TextStyle},
     Font, FontSettings,
 };
-use miniquad::*;
+use freetype as ft;
+use miniquad::{
+    MouseButton,
+    RenderingBackend,
+    TextureId,
+    BufferId,
+    window,
+    UniformType,
+    UniformDesc,
+    ShaderMeta,
+    Pipeline,
+    PipelineParams,
+    Backend,
+    ShaderSource,
+    Equation,
+    BufferLayout,
+    BlendValue,
+    VertexFormat,
+    VertexAttribute,
+    BlendState,
+    BlendFactor,
+    BufferType,
+    BufferUsage,
+    BufferSource,
+    Bindings,
+    PassAction,
+    EventHandler,
+    KeyCode,
+    KeyMods,
+    conf,
+};
 use std::{
     array::IntoIter,
     fmt,
@@ -36,6 +66,11 @@ impl MouseButtonAsU8 for MouseButton {
     }
 }
 
+type Color = [f32; 4];
+
+const COLOR_RED: Color = [1., 0., 0., 1.];
+const COLOR_BLUE: Color = [0., 0., 1., 1.];
+
 #[derive(Debug, SerialEncodable, SerialDecodable)]
 #[repr(C)]
 struct Vertex {
@@ -92,7 +127,7 @@ enum GraphicsMethodEvent {
     DeleteMesh,
 }
 
-struct Stage {
+struct Stage<'a> {
     ctx: Box<dyn RenderingBackend>,
     pipeline: Pipeline,
 
@@ -101,6 +136,7 @@ struct Stage {
     textures: ResourceManager<TextureId>,
     meshes: ResourceManager<Mesh>,
     font: Font,
+    ft_face: ft::Face<&'a [u8]>,
 
     method_recvr: mpsc::Receiver<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
     method_sender: mpsc::SyncSender<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
@@ -108,10 +144,10 @@ struct Stage {
     last_draw_time: Option<Instant>,
 }
 
-impl Stage {
+impl<'a> Stage<'a> {
     const WHITE_TEXTURE_ID: ResourceId = 0;
 
-    pub fn new(scene_graph: SceneGraphPtr) -> Stage {
+    pub fn new(scene_graph: SceneGraphPtr) -> Stage<'a> {
         let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
 
         let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
@@ -158,7 +194,8 @@ impl Stage {
 
         let (method_sender, method_recvr) = mpsc::sync_channel(100);
 
-        let font = {
+        //let font = {
+            /*
             let mut scene_graph = scene_graph.lock().unwrap();
 
             let font = scene_graph.add_node("font", SceneNodeType::Fonts);
@@ -166,15 +203,26 @@ impl Stage {
             scene_graph.link(font_id, SceneGraph::ROOT_ID).unwrap();
 
             let inter_regular = scene_graph.add_node("inter-regular", SceneNodeType::Font);
+            */
 
-            let font = include_bytes!("../Inter-Regular.ttf") as &[u8];
-            let font = Font::from_bytes(font, FontSettings::default()).unwrap();
+            let font_data = include_bytes!("../Inter-Regular.otf") as &[u8];
+            let font = Font::from_bytes(font_data, FontSettings::default()).unwrap();
             //let line_metrics = font.horizontal_line_metrics(1.).unwrap();
             //inter_regular.add_property_f32("ascent", line_metrics.ascent).unwrap();
             //inter_regular.add_property_f32("descent", line_metrics.descent).unwrap();
             //inter_regular.add_property_f32("line_gap", line_metrics.line_gap).unwrap();
             //inter_regular.add_property_f32("new_line_size", line_metrics.new_line_size).unwrap();
 
+            let face = harfbuzz_rs::Face::from_bytes(font_data, 0);
+            //unsafe {
+            //    let raw_font = hb_font_create(face.as_raw());
+            //    let hb = harfbuzz_rs::Owned::from_raw(raw_font);
+            //}
+
+            let ftlib = ft::Library::init().unwrap();
+            let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
+
+            /*
             let sender = method_sender.clone();
             let inter_regular_id = inter_regular.id;
             let method_fn = Box::new(move |arg_data, response_fn| {
@@ -199,6 +247,7 @@ impl Stage {
             scene_graph.link(inter_regular_id, font_id).unwrap();
             font
         };
+            */
 
         let mut stage = Stage {
             ctx,
@@ -207,6 +256,7 @@ impl Stage {
             textures,
             meshes: ResourceManager::new(),
             font,
+            ft_face,
             method_recvr,
             method_sender,
             last_draw_time: None,
@@ -593,6 +643,8 @@ struct RenderContext<'a> {
     proj: glam::Mat4,
     textures: &'a ResourceManager<TextureId>,
     draw_calls: Vec<DeferredDraw>,
+    font: &'a Font,
+    ft_face: &'a ft::Face<&'a [u8]>,
 }
 
 impl<'a> RenderContext<'a> {
@@ -666,7 +718,7 @@ impl<'a> RenderContext<'a> {
 
         // get the rectangle
         // make sure it's inside the parent's rect
-        for child in layer.get_children(&[SceneNodeType::RenderMesh]) {
+        for child in layer.get_children(&[SceneNodeType::RenderMesh, SceneNodeType::RenderText]) {
             // x, y, w, h as pixels
 
             // note that (x, y) is offset by layer rect so it is the pos within layer
@@ -683,6 +735,11 @@ impl<'a> RenderContext<'a> {
                         error!("error rendering mesh '{}': {}", child.name, err);
                     }
                 }
+                SceneNodeType::RenderText => {
+                    if let Err(err) = self.render_text(child.id, &rect) {
+                        error!("error rendering text '{}': {}", child.name, err);
+                    }
+                }
                 _ => panic!("render_layer(): unknown type"),
             }
         }
@@ -741,8 +798,8 @@ impl<'a> RenderContext<'a> {
         Ok(Rectangle::from_array(rect))
     }
 
-    fn render_mesh(&mut self, mesh_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
-        let mesh = self.scene_graph.get_node(mesh_id).unwrap();
+    fn render_mesh(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
+        let mesh = self.scene_graph.get_node(node_id).unwrap();
 
         let z_index = mesh.get_property_u32("z_index")?;
 
@@ -817,6 +874,387 @@ impl<'a> RenderContext<'a> {
 
         Ok(())
     }
+
+    fn render_box_with_texture(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, texture: TextureId) {
+        let vertices: [Vertex; 4] = [
+            // top left
+            Vertex { pos: [x1, y1], color, uv: [0., 0.] },
+            // top right
+            Vertex { pos: [x2, y1], color, uv: [1., 0.] },
+            // bottom left
+            Vertex { pos: [x1, y2], color, uv: [0., 1.] },
+            // bottom right
+            Vertex { pos: [x2, y2], color, uv: [1., 1.] },
+        ];
+
+        //debug!("screen size: {:?}", window::screen_size());
+        let vertex_buffer = self.ctx.new_buffer(
+            BufferType::VertexBuffer,
+            BufferUsage::Immutable,
+            BufferSource::slice(&vertices),
+        );
+
+        let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
+        let index_buffer = self.ctx.new_buffer(
+            BufferType::IndexBuffer,
+            BufferUsage::Immutable,
+            BufferSource::slice(&indices),
+        );
+
+        let bindings =
+            Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
+
+        self.ctx.apply_bindings(&bindings);
+        self.ctx.draw(0, 6, 1);
+    }
+
+    fn render_box(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
+        let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap();
+        self.render_box_with_texture(x1, y1, x2, y2, color, *texture)
+    }
+
+    fn hline(&mut self, min_x: f32, max_x: f32, y: f32, color: Color, w: f32) {
+        self.render_box(min_x, y - w/2., max_x, y + w/2., color)
+    }
+
+    fn vline(&mut self, x: f32, min_y: f32, max_y: f32, color: Color, w: f32) {
+        self.render_box(x - w/2., min_y, x + w/2., max_y, color)
+    }
+
+    fn outline(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, w: f32) {
+        // top
+        self.render_box(x1, y1, x2, y1 + w, color);
+        // left
+        self.render_box(x1, y1, x1 + w, y2, color);
+        // right
+        self.render_box(x2 - w, y1, x2, y2, color);
+        // bottom
+        self.render_box(x1, y2 - w, x2, y2, color);
+    }
+
+    fn render_text(&mut self, mesh_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
+        let node = self.scene_graph.get_node(mesh_id).unwrap();
+
+        let z_index = node.get_property_u32("z_index")?;
+        let text = node.get_property_str("text")?;
+        let overflow = node.get_property_enum("overflow")?;
+        let font_size = node.get_property_f32("font_size")?;
+        let debug = node.get_property_bool("debug")?;
+        let rect = Self::get_dim(node, layer_rect)?;
+
+        let layer_w = layer_rect.w as f32;
+        let layer_h = layer_rect.h as f32;
+        let off_x = rect.x / layer_w;
+        let off_y = rect.y / layer_h;
+        // Use absolute pixel scale
+        let scale_x = 1. / layer_w;
+        let scale_y = 1. / layer_h;
+        //let model = glam::Mat4::IDENTITY;
+        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
+            glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
+
+        let mut uniforms_data = [0u8; 128];
+        let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) };
+        uniforms_data[0..64].copy_from_slice(&data);
+        let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
+        uniforms_data[64..].copy_from_slice(&data);
+        assert_eq!(128, 2 * UniformType::Mat4.size());
+
+        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();
+
+        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, &[]);
+
+        let positions = output.get_glyph_positions();
+        let infos = output.get_glyph_infos();
+
+        let mut current_x = 0.;
+        let mut current_y = 0.;
+        for (position, info) in positions.iter().zip(infos) {
+            let gid = info.codepoint;
+
+            //let x = current_x + position.x_offset as f32 / 64.;
+            //let y = current_y + position.y_offset as f32 / 64.;
+
+            self.ft_face.load_glyph(gid, ft::face::LoadFlag::COLOR).unwrap();
+
+            let glyph = self.ft_face.glyph();
+            glyph.render_glyph(ft::RenderMode::Normal).unwrap();
+
+            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 twidth = 2_usize.pow((width as f32).log2().ceil() as u32);
+            //let theight = 2_usize.pow((height as f32).log2().ceil() as u32);
+            //assert!(twidth >= width as usize);
+            //assert!(theight >= height as usize);
+            //assert_eq!(buffer.len(), (4 * width * height) as usize);
+
+            //let mut tdata = vec![];
+            //tdata.resize(twidth * theight * 4, 0u8);
+            //for iy in 0..height as usize {
+            //    let src_start = 4 * iy * width as usize;
+            //    let src_end = src_start + 4 * width as usize;
+            //    let dest_start = 4 * iy * twidth;
+            //    let dest_end = dest_start + 4 * width as usize;
+            //    let src = &buffer[src_start..src_end];
+            //    tdata[dest_start..dest_end].clone_from_slice(src);
+            //}
+            //// Convert from BGRA to RGBA
+            //for i in 0..(twidth*theight) {
+            //    let idx = i*4;
+            //    let b = tdata[idx];
+            //    let r = tdata[idx + 2];
+            //    tdata[idx] = r;
+            //    tdata[idx + 2] = b;
+            //}
+            // Add an alpha channel
+            // Convert from greyscale to RGBA8
+            let tdata: Vec<_> =
+                buffer.iter().flat_map(|coverage| {
+                    let α = *coverage;
+                    vec![α, α, α, α]
+                }).collect();
+
+            // UV coords
+            let s0 = 0.;
+            let t0 = 0.;
+            let s1 = 1.;
+            let t1 = 1.;
+            //let s1 = width as f32 / twidth as f32;
+            //let t1 = height as f32 / theight as f32;
+
+            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;
+
+            // Flip the axis
+            //let y0 = 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;
+
+            //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!();
+
+            //    self.render_glyph(gid, font_size, x as f32, y as f32)?;
+            let color = [1., 1., 1., 1.];
+            // faces: 021, 123
+            let vertices: [Vertex; 4] = [
+                // top left
+                Vertex { pos: [x1, y1], color, uv: [0., 0.] },
+                // top right
+                Vertex { pos: [x2, y1], color, uv: [1., 0.] },
+                // bottom left
+                Vertex { pos: [x1, y2], color, uv: [0., 1.] },
+                // bottom right
+                Vertex { pos: [x2, y2], color, uv: [1., 1.] },
+            ];
+
+            //debug!("screen size: {:?}", window::screen_size());
+            let vertex_buffer = self.ctx.new_buffer(
+                BufferType::VertexBuffer,
+                BufferUsage::Immutable,
+                BufferSource::slice(&vertices),
+            );
+
+           // let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
+            let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
+            let index_buffer = self.ctx.new_buffer(
+                BufferType::IndexBuffer,
+                BufferUsage::Immutable,
+                BufferSource::slice(&indices),
+            );
+
+            let texture = self.ctx.new_texture_from_rgba8(
+                width as u16,
+                height as u16,
+                &tdata,
+            );
+
+            let bindings =
+                Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
+
+            self.ctx.apply_bindings(&bindings);
+            self.ctx.draw(0, 6, 1);
+
+            self.ctx.delete_texture(texture);
+
+            if debug {
+                self.outline(x1, y1, x2, y2, COLOR_BLUE, 1.);
+            }
+        }
+        if debug {
+            self.hline(0., current_x, 0., COLOR_RED, 1.);
+        }
+
+        /*
+        let vertex_buffer = self.ctx.new_buffer(
+            BufferType::VertexBuffer,
+            BufferUsage::Immutable,
+            BufferSource::slice(&verts),
+        );
+
+        let bufsrc = unsafe {
+            BufferSource::pointer(
+                faces.as_ptr() as _,
+                std::mem::size_of_val(&faces[..]),
+                std::mem::size_of::<u32>(),
+            )
+        };
+
+        let index_buffer =
+            self.ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, bufsrc);
+
+        // temp
+        let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap();
+
+        let rect = Self::get_dim(mesh, layer_rect)?;
+        //debug!("mesh rect: {:?}", rect);
+
+        let layer_w = layer_rect.w as f32;
+        let layer_h = layer_rect.h as f32;
+        let off_x = rect.x / layer_w;
+        let off_y = rect.y / layer_h;
+        let scale_x = rect.w / layer_w;
+        let scale_y = rect.h / layer_h;
+        //let model = glam::Mat4::IDENTITY;
+        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
+            glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
+
+        let mut uniforms_data = [0u8; 128];
+        let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) };
+        uniforms_data[0..64].copy_from_slice(&data);
+        let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
+        uniforms_data[64..].copy_from_slice(&data);
+        assert_eq!(128, 2 * UniformType::Mat4.size());
+
+        if z_index > 0 {
+            let draw_call = DeferredDraw {
+                z_index,
+                vertex_buffer,
+                index_buffer,
+                texture: *texture,
+                uniforms_data,
+                faces_len: faces.len(),
+            };
+            self.draw_calls.push(draw_call);
+            return Ok(())
+        }
+
+        let bindings =
+            Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![*texture] };
+
+        self.ctx.apply_bindings(&bindings);
+
+        self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
+
+        self.ctx.draw(0, 3 * faces.len() as i32, 1);
+
+        self.ctx.delete_buffer(index_buffer);
+        self.ctx.delete_buffer(vertex_buffer);
+        */
+
+        Ok(())
+    }
+
+    fn render_glyph(&mut self, glyph_id: u32, font_size: f32, x: f32, y: f32) -> Result<()> {
+
+        let (metrics, text_bitmap) = self.font.rasterize_indexed_subpixel(glyph_id as u16, font_size);
+        assert!(text_bitmap.len() % 3 == 0);
+        let bmp_len = text_bitmap.len() / 3;
+        let mut bmp = Vec::<u8>::with_capacity(4 * bmp_len);
+        for i in 0..bmp_len {
+            let r = text_bitmap[3*i];
+            let g = text_bitmap[3*i + 1];
+            let b = text_bitmap[3*i + 2];
+
+            let α = if r == 0 && g == 0 && b == 0 {
+                0
+            } else {
+                255
+            };
+
+            bmp.push(r);
+            bmp.push(g);
+            bmp.push(b);
+            bmp.push(α);
+        }
+        drop(text_bitmap);
+
+        let (w, h) = (metrics.width as f32, metrics.height as f32);
+        let color = [1., 1., 1., 1.];
+        //    0             1
+        // (-1, 1) ----- (1, 1)
+        //    |          /  |
+        //    |        /    |
+        //    |      /      |
+        //    |    /        |
+        // (-1, -1) ---- (1, -1)
+        //    2             3
+        //
+        // faces: 021, 123
+        let vertices: [Vertex; 4] = [
+            // top left
+            Vertex { pos: [x, y - h], color, uv: [0., 0.] },
+            // top right
+            Vertex { pos: [x + w, y - h], color, uv: [1., 0.] },
+            // bottom left
+            Vertex { pos: [x, y], color, uv: [0., 1.] },
+            // bottom right
+            Vertex { pos: [x + w, y], color, uv: [1., 1.] },
+        ];
+
+        //debug!("screen size: {:?}", window::screen_size());
+        let vertex_buffer = self.ctx.new_buffer(
+            BufferType::VertexBuffer,
+            BufferUsage::Immutable,
+            BufferSource::slice(&vertices),
+        );
+
+        let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
+        let index_buffer = self.ctx.new_buffer(
+            BufferType::IndexBuffer,
+            BufferUsage::Immutable,
+            BufferSource::slice(&indices),
+        );
+
+        let texture = self.ctx.new_texture_from_rgba8(
+            metrics.width as u16,
+            metrics.height as u16,
+            &bmp,
+        );
+
+        let bindings =
+            Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
+
+        self.ctx.apply_bindings(&bindings);
+        self.ctx.draw(0, 6, 1);
+
+        self.ctx.delete_texture(texture);
+        Ok(())
+    }
 }
 
 /*
@@ -841,7 +1279,7 @@ fn get_text_props(render_text: &SceneNode) -> Result<(String, f32, [f32; 4])> {
 }
 */
 
-impl EventHandler for Stage {
+impl<'a> EventHandler for Stage<'a> {
     fn update(&mut self) {
         if self.last_draw_time.is_none() {
             return
@@ -903,6 +1341,8 @@ impl EventHandler for Stage {
             proj,
             textures: &self.textures,
             draw_calls: vec![],
+            font: &self.font,
+            ft_face: &self.ft_face,
         };
 
         render_context.render_window();

+ 3 - 0
bin/darkwallet/src/scene.rs

@@ -429,6 +429,9 @@ impl SceneNode {
     pub fn get_property_str(&self, name: &str) -> Result<String> {
         self.get_property(name).ok_or(Error::PropertyNotFound)?.get_str(0)
     }
+    pub fn get_property_enum(&self, name: &str) -> Result<String> {
+        self.get_property(name).ok_or(Error::PropertyNotFound)?.get_enum(0)
+    }
     pub fn get_property_node_id(&self, name: &str) -> Result<SceneNodeId> {
         self.get_property(name).ok_or(Error::PropertyNotFound)?.get_node_id(0)
     }