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

app: migrate ui/text to parley/swash

darkfi 1 год назад
Родитель
Сommit
a290a3a194

+ 204 - 0
bin/app/Cargo.lock

@@ -57,6 +57,12 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
 
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
 [[package]]
 name = "amplify"
 version = "4.8.0"
@@ -809,6 +815,20 @@ name = "bytemuck"
 version = "1.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
 
 [[package]]
 name = "byteorder"
@@ -968,6 +988,12 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "color"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c387f6cef110ee8eaf12fca5586d3d303c07c594f4a5f02c768b6470b70dbd"
+
 [[package]]
 name = "color_quant"
 version = "1.1.0"
@@ -1277,6 +1303,7 @@ dependencies = [
  "log",
  "miniquad",
  "parking_lot 0.12.3",
+ "parley",
  "rand",
  "regex",
  "rusqlite",
@@ -1284,9 +1311,11 @@ dependencies = [
  "simplelog",
  "sled-overlay",
  "smol",
+ "swash",
  "thiserror 2.0.12",
  "tor-dirmgr 0.26.0",
  "url",
+ "zeno",
  "zeromq",
 ]
 
@@ -1968,6 +1997,52 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "font-types"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d868ec188a98bb014c606072edd47e52e7ab7297db943b0b28503121e1d037bd"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "fontconfig-cache-parser"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7f8afb20c8069fd676d27b214559a337cc619a605d25a87baa90b49a06f3b18"
+dependencies = [
+ "bytemuck",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "fontique"
+version = "0.3.0"
+source = "git+https://github.com/linebender/parley.git#4bb6859668815e717491ce55e6fd9390e70b0e9a"
+dependencies = [
+ "bytemuck",
+ "fontconfig-cache-parser",
+ "hashbrown 0.15.2",
+ "icu_locid",
+ "memmap2",
+ "objc2-core-foundation",
+ "objc2-core-text",
+ "objc2-foundation",
+ "peniko",
+ "read-fonts",
+ "roxmltree",
+ "smallvec",
+ "windows",
+ "windows-core 0.58.0",
+]
+
 [[package]]
 name = "foreign-types"
 version = "0.5.0"
@@ -2411,6 +2486,11 @@ name = "hashbrown"
 version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
 
 [[package]]
 name = "hashlink"
@@ -2910,6 +2990,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "kurbo"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
 [[package]]
 name = "kv-log-macro"
 version = "1.0.7"
@@ -3339,6 +3429,50 @@ dependencies = [
  "malloc_buf",
 ]
 
+[[package]]
+name = "objc2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
+dependencies = [
+ "bitflags 2.9.0",
+]
+
+[[package]]
+name = "objc2-core-text"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fde15abfe00bf9f1eda220addbdfa6c62b0e5595e98208e8cdbc2ec0f6970a6"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2",
+]
+
 [[package]]
 name = "oid-registry"
 version = "0.8.1"
@@ -3491,6 +3625,18 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "parley"
+version = "0.3.0"
+source = "git+https://github.com/linebender/parley.git#4bb6859668815e717491ce55e6fd9390e70b0e9a"
+dependencies = [
+ "fontique",
+ "hashbrown 0.15.2",
+ "peniko",
+ "skrifa",
+ "swash",
+]
+
 [[package]]
 name = "pasta_curves"
 version = "0.5.1"
@@ -3531,6 +3677,17 @@ dependencies = [
  "base64ct",
 ]
 
+[[package]]
+name = "peniko"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ec061f435b573ff602b2c5690f1a5bfcf461327194654cfbfce4412b95cf2a1"
+dependencies = [
+ "color",
+ "kurbo",
+ "smallvec",
+]
+
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
@@ -3970,6 +4127,16 @@ dependencies = [
  "yasna",
 ]
 
+[[package]]
+name = "read-fonts"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f9e8a4f503e5c8750e4cd3b32a4e090035c46374b305a15c70bad833dca05f"
+dependencies = [
+ "bytemuck",
+ "font-types",
+]
+
 [[package]]
 name = "redox_syscall"
 version = "0.2.16"
@@ -4105,6 +4272,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
+
 [[package]]
 name = "rsa"
 version = "0.9.8"
@@ -4532,6 +4705,16 @@ dependencies = [
  "walkdir",
 ]
 
+[[package]]
+name = "skrifa"
+version = "0.26.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc1aa86c26dbb1b63875a7180aa0819709b33348eb5b1491e4321fae388179d"
+dependencies = [
+ "bytemuck",
+ "read-fonts",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -4817,6 +5000,16 @@ version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
+[[package]]
+name = "swash"
+version = "0.2.1"
+source = "git+https://github.com/valadaptive/swash?branch=tight-bounds#2a6429fb81b692d7345a78986354f939f424d1f3"
+dependencies = [
+ "skrifa",
+ "yazi",
+ "zeno",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -7532,6 +7725,12 @@ dependencies = [
  "time",
 ]
 
+[[package]]
+name = "yazi"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
+
 [[package]]
 name = "yoke"
 version = "0.7.5"
@@ -7556,6 +7755,11 @@ dependencies = [
  "synstructure",
 ]
 
+[[package]]
+name = "zeno"
+version = "0.3.2"
+source = "git+https://github.com/valadaptive/zeno?branch=tight-bounds#696f94184a1af5c0411c282045549264236ff881"
+
 [[package]]
 name = "zerocopy"
 version = "0.7.35"

+ 7 - 0
bin/app/Cargo.toml

@@ -49,6 +49,10 @@ simplelog = "0.12.2"
 file-rotate = "0.7.6"
 parking_lot = { version = "0.12.3", features = ["nightly"] }
 regex = "1.11"
+parley = { git = "https://github.com/linebender/parley.git" }
+swash = { git = "https://github.com/valadaptive/swash", branch = "tight-bounds" }
+# Use same zeno specified by swash
+zeno = { git = "https://github.com/valadaptive/zeno", branch = "tight-bounds" }
 
 [features]
 emulate-android = []
@@ -69,6 +73,9 @@ halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v4"}
 # This patch didn't work for me
 #dirs-sys = {git="https://github.com/sbechet/dirs-sys-rs.git"}
 
+# Once merged, we can remove this
+swash = { git = "https://github.com/valadaptive/swash", branch = "tight-bounds" }
+
 [target.'cfg(not(target_os = "android"))'.dependencies]
 dirs = "5.0.1"
 

+ 1 - 1
bin/app/Dockerfile

@@ -10,7 +10,7 @@ ENV RUSTUP_HOME=/usr/local/rustup \
 
 RUN curl -o rustup.sh --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs
 RUN chmod +x rustup.sh
-RUN ./rustup.sh -y --default-toolchain nightly-2024-12-05
+RUN ./rustup.sh -y --default-toolchain nightly-2025-03-27
 
 #RUN rustup toolchain install nightly
 #RUN rustup default nightly

+ 2 - 2
bin/app/Makefile

@@ -18,8 +18,8 @@ RELEASE_FEATURES = --features=enable-plugins
 DEBUG_FEATURES = --features=enable-filelog,enable-plugins
 
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,emulate-android
-DEV_FEATURES = --features=enable-filelog,enable-netdebug,enable-plugins
-#DEV_FEATURES = --features=enable-filelog,enable-netdebug
+#DEV_FEATURES = --features=enable-filelog,enable-netdebug,enable-plugins
+DEV_FEATURES = --features=enable-filelog,enable-netdebug
 
 default: build-release
 	./darkfi-app

+ 2 - 1
bin/app/rust-toolchain.toml

@@ -1,2 +1,3 @@
 [toolchain]
-channel = "nightly-2024-12-05"
+channel = "nightly-2025-03-27"
+#channel = "nightly-2024-12-05"

+ 1 - 1
bin/app/src/app/mod.rs

@@ -204,7 +204,7 @@ impl App {
         self.sg_root.clone().link(window.clone());
         self.sg_root.clone().link(setting_root.clone());
 
-        schema::make(&self, window.clone()).await;
+        schema::test::make(&self, window.clone()).await;
 
         d!("Schema loaded");
 

+ 7 - 2
bin/app/src/app/schema/test.rs

@@ -106,6 +106,7 @@ pub async fn make(app: &App, window: SceneNodePtr) {
         node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
     layer_node.clone().link(node);
 
+    /*
     // Create button bg
     let node = create_vector_art("btnbg");
     let prop = node.get_property("rect").unwrap();
@@ -255,6 +256,7 @@ pub async fn make(app: &App, window: SceneNodePtr) {
     node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
     let node = node.setup(|me| Image::new(me, app.render_api.clone(), app.ex.clone())).await;
     layer_node.clone().link(node);
+    */
 
     // Create some text
     let node = create_text("label");
@@ -269,8 +271,9 @@ pub async fn make(app: &App, window: SceneNodePtr) {
         atom,
         Role::App,
         "text",
-        "\u{f0007}",
-        //"hel \u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} 123 '\u{01f44d}\u{01f3fe}' br",
+        // Monero custom icon
+        //"\u{f0007}",
+        "yoyo 🚀 'پل خواجو' he", //"hel \u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} 123 '\u{01f44d}\u{01f3fe}' br",
     )
     .unwrap();
     //node.set_property_str(atom, Role::App, "text", "anon1").unwrap();
@@ -294,6 +297,7 @@ pub async fn make(app: &App, window: SceneNodePtr) {
         .await;
     layer_node.clone().link(node);
 
+    /*
     // Text edit
     let node = create_editbox("editz");
     node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
@@ -532,4 +536,5 @@ pub async fn make(app: &App, window: SceneNodePtr) {
         })
         .await;
     layer_node.clone().link(node);
+    */
 }

+ 1 - 0
bin/app/src/text/mod.rs

@@ -35,6 +35,7 @@ use crate::gfx::Rectangle;
 
 mod atlas;
 pub use atlas::{make_texture_atlas, Atlas, RenderedAtlas};
+pub mod atlas2;
 mod ft;
 use ft::{render_glyph, FreetypeFace, Sprite, SpritePtr};
 mod shape;

+ 105 - 9
bin/app/src/ui/text.rs

@@ -116,6 +116,105 @@ impl Text {
         Pimpl::Text(self_)
     }
 
+    fn regen_mesh2(&self) -> Vec<GfxDrawInstruction> {
+        let text = self.text.get();
+        let font_size = self.font_size.get();
+        let text_color = self.text_color.get();
+        let window_scale = self.window_scale.get();
+
+        let mut layout_cx = parley::LayoutContext::new();
+        let mut font_cx = parley::FontContext::new();
+
+        let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, window_scale);
+
+        let brush_style = parley::StyleProperty::Brush(text_color);
+        builder.push_default(brush_style);
+
+        let font_stack = parley::FontStack::from("system-ui");
+        builder.push_default(font_stack);
+        builder.push_default(parley::StyleProperty::LineHeight(2.));
+        builder.push_default(parley::StyleProperty::FontSize(font_size));
+
+        let mut layout: parley::Layout<Color> = builder.build(&text);
+        // Perform layout (including bidi resolution and shaping) with start alignment
+        layout.break_all_lines(None);
+        layout.align(None, parley::Alignment::Start, parley::AlignmentOptions::default());
+
+        let mut scale_cx = swash::scale::ScaleContext::new();
+        let mut run_idx = 0;
+        let mut instrs = vec![];
+        for line in layout.lines() {
+            for item in line.items() {
+                match item {
+                    parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
+                        let mesh = self.render_glyph_run(&mut scale_cx, &glyph_run, &text, run_idx);
+                        instrs.push(GfxDrawInstruction::Draw(mesh));
+                        run_idx += 1;
+                    }
+                    parley::PositionedLayoutItem::InlineBox(_) => {}
+                }
+            }
+        }
+        instrs
+    }
+
+    fn render_glyph_run(
+        &self,
+        scale_ctx: &mut swash::scale::ScaleContext,
+        glyph_run: &parley::GlyphRun<'_, Color>,
+        text: &str,
+        run_idx: usize,
+    ) -> GfxDrawMesh {
+        let mut run_x = glyph_run.offset();
+        let run_y = glyph_run.baseline();
+        let style = glyph_run.style();
+        let color = style.brush;
+
+        let run = glyph_run.run();
+        let text = &text[run.text_range()];
+        t!("render_glyph_run '{text}' run_idx={run_idx}");
+
+        let font = run.font();
+        let font_size = run.font_size();
+        let normalized_coords = run.normalized_coords();
+        let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
+
+        let mut scaler = scale_ctx
+            .builder(font_ref)
+            .size(font_size)
+            .hint(true)
+            .normalized_coords(normalized_coords)
+            .build();
+
+        let mut atlas = text::atlas2::Atlas::new(scaler, &self.render_api);
+        for glyph in glyph_run.glyphs() {
+            atlas.push_glyph(glyph);
+        }
+        atlas.dump(&format!("/tmp/atlas_{run_idx}.png"));
+        let atlas = atlas.make();
+
+        let mut mesh = MeshBuilder::new();
+        for glyph in glyph_run.glyphs() {
+            let glyph_inf = atlas.fetch_uv(glyph.id).expect("missing glyph UV rect");
+
+            let glyph_x = run_x + glyph.x;
+            let glyph_y = run_y - glyph.y;
+            run_x += glyph.advance;
+
+            let glyph_rect = Rectangle::new(
+                glyph_x + glyph_inf.place.left as f32,
+                glyph_y - glyph_inf.place.top as f32,
+                glyph_inf.place.width as f32,
+                glyph_inf.place.height as f32,
+            );
+
+            let color = if glyph_inf.is_color { COLOR_WHITE } else { color };
+
+            mesh.draw_box(&glyph_rect, color, &glyph_inf.uv_rect);
+        }
+        mesh.alloc(&self.render_api).draw_with_texture(atlas.texture)
+    }
+
     fn regen_mesh(&self) -> TextRenderInfo {
         let text = self.text.get();
         let font_size = self.font_size.get();
@@ -174,27 +273,24 @@ impl Text {
         self.rect.eval(&parent_rect).ok()?;
         let rect = self.rect.get();
 
-        let render_info = self.regen_mesh();
+        let mut instrs = vec![GfxDrawInstruction::Move(rect.pos())];
+        instrs.append(&mut self.regen_mesh2());
 
+        /*
+        let render_info = self.regen_mesh();
         let mesh = GfxDrawMesh {
             vertex_buffer: render_info.mesh.vertex_buffer,
             index_buffer: render_info.mesh.index_buffer,
             texture: Some(render_info.texture),
             num_elements: render_info.mesh.num_elements,
         };
+        */
 
         Some(DrawUpdate {
             key: self.dc_key,
             draw_calls: vec![(
                 self.dc_key,
-                GfxDrawCall {
-                    instrs: vec![
-                        GfxDrawInstruction::Move(rect.pos()),
-                        GfxDrawInstruction::Draw(mesh),
-                    ],
-                    dcs: vec![],
-                    z_index: self.z_index.get(),
-                },
+                GfxDrawCall { instrs, dcs: vec![], z_index: self.z_index.get() },
             )],
         })
     }