Lay Groundwork for Rust-based SVG rasterization (#1422)

* Add functions for constructing a usvg tree

* Actually encode the image in the usvg tree

* Implement path translation

* Render document using resvg
This commit is contained in:
Dennis Kobert 2023-09-30 11:43:24 +02:00 committed by Keavon Chambers
parent 34f2d61257
commit e1cdb2242d
10 changed files with 585 additions and 46 deletions

443
Cargo.lock generated
View File

@ -1111,6 +1111,12 @@ dependencies = [
"syn 2.0.29",
]
[[package]]
name = "data-url"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5"
[[package]]
name = "deranged"
version = "0.3.8"
@ -1346,6 +1352,15 @@ dependencies = [
"libc",
]
[[package]]
name = "euclid"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787"
dependencies = [
"num-traits",
]
[[package]]
name = "event-listener"
version = "2.5.3"
@ -1392,6 +1407,14 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "fello"
version = "0.1.0"
source = "git+https://github.com/dfrg/fount?rev=dadbcf75695f035ca46766bfd60555d05bd421b1#dadbcf75695f035ca46766bfd60555d05bd421b1"
dependencies = [
"read-fonts",
]
[[package]]
name = "fern"
version = "0.6.2"
@ -1440,6 +1463,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "flume"
version = "0.10.14"
@ -1459,6 +1488,35 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "font-types"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6978d65d61022aa249fefdd914dc8215757f617f1a697c496ef6b42013366567"
[[package]]
name = "fontconfig-parser"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4"
dependencies = [
"roxmltree",
]
[[package]]
name = "fontdb"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8d8cbea8f21307d7e84bca254772981296f058a1d36b461bf4d83a7499fc9e"
dependencies = [
"fontconfig-parser",
"log",
"memmap2 0.6.2",
"slotmap",
"tinyvec",
"ttf-parser 0.19.1",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -2077,17 +2135,18 @@ dependencies = [
"glam",
"image",
"js-sys",
"kurbo",
"kurbo 0.9.5 (git+https://github.com/linebender/kurbo.git)",
"log",
"node-macro",
"num-derive",
"num-traits",
"rand_chacha 0.3.1",
"rustybuzz",
"rustybuzz 0.8.0",
"serde",
"specta",
"spin 0.9.8",
"spirv-std",
"usvg 0.35.0",
"wasm-bindgen",
"web-sys",
]
@ -2117,12 +2176,15 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"reqwest",
"resvg",
"rustc-hash",
"serde",
"serde_json",
"tempfile",
"tokio",
"url",
"vello",
"vello_svg",
"vulkan-executor",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -2162,9 +2224,9 @@ dependencies = [
"graphene-core",
"graphene-std",
"image",
"kurbo",
"kurbo 0.9.5 (git+https://github.com/linebender/kurbo.git)",
"log",
"rustybuzz",
"rustybuzz 0.8.0",
"serde",
"specta",
]
@ -2289,6 +2351,16 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "guillotiere"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
dependencies = [
"euclid",
"svg_fmt",
]
[[package]]
name = "h2"
version = "0.3.21"
@ -2604,6 +2676,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
version = "1.9.3"
@ -2827,6 +2905,15 @@ dependencies = [
"selectors",
]
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]]
name = "kurbo"
version = "0.9.5"
@ -3027,6 +3114,15 @@ dependencies = [
"libc",
]
[[package]]
name = "memmap2"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@ -3683,7 +3779,7 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4"
dependencies = [
"ttf-parser",
"ttf-parser 0.19.1",
]
[[package]]
@ -3752,6 +3848,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "peniko"
version = "0.1.0"
source = "git+https://github.com/linebender/peniko?rev=cafdac9a211a0fb2fec5656bd663d1ac770bcc81#cafdac9a211a0fb2fec5656bd663d1ac770bcc81"
dependencies = [
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
@ -3866,6 +3971,12 @@ dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project"
version = "1.1.3"
@ -4183,6 +4294,21 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "read-fonts"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87d08214643b2df95b0b3955cd9f264bcfab22b73470b83df4992df523b4d6eb"
dependencies = [
"font-types",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -4317,6 +4443,23 @@ dependencies = [
"winreg 0.50.0",
]
[[package]]
name = "resvg"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6554f47c38eca56827eea7f285c2a3018b4e12e0e195cc105833c008be338f1"
dependencies = [
"gif",
"jpeg-decoder",
"log",
"pico-args",
"png",
"rgb",
"svgtypes",
"tiny-skia 0.10.0",
"usvg 0.35.0",
]
[[package]]
name = "rfd"
version = "0.10.0"
@ -4341,6 +4484,15 @@ dependencies = [
"windows 0.37.0",
]
[[package]]
name = "rgb"
version = "0.8.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -4368,6 +4520,28 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "rosvgtree"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad747e7384940e7bf33b15ba433b7bad9f44c0c6d5287a67c2cb22cd1743d497"
dependencies = [
"log",
"roxmltree",
"simplecss",
"siphasher",
"svgtypes",
]
[[package]]
name = "roxmltree"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8"
dependencies = [
"xmlparser",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -4453,6 +4627,22 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "rustybuzz"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a"
dependencies = [
"bitflags 1.3.2",
"bytemuck",
"smallvec",
"ttf-parser 0.18.1",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-general-category",
"unicode-script",
]
[[package]]
name = "rustybuzz"
version = "0.8.0"
@ -4462,7 +4652,7 @@ dependencies = [
"bitflags 1.3.2",
"bytemuck",
"smallvec",
"ttf-parser",
"ttf-parser 0.19.1",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-general-category",
@ -4538,9 +4728,9 @@ checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09"
dependencies = [
"ab_glyph",
"log",
"memmap2",
"memmap2 0.5.10",
"smithay-client-toolkit",
"tiny-skia",
"tiny-skia 0.8.4",
]
[[package]]
@ -4819,6 +5009,15 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.11"
@ -4903,7 +5102,7 @@ dependencies = [
"dlib",
"lazy_static",
"log",
"memmap2",
"memmap2 0.5.10",
"nix 0.24.3",
"pkg-config",
"wayland-client",
@ -5070,6 +5269,9 @@ name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "string_cache"
@ -5103,6 +5305,22 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "svg_fmt"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
[[package]]
name = "svgtypes"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed4b0611e7f3277f68c0fa18e385d9e2d26923691379690039548f867cef02a7"
dependencies = [
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"siphasher",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -5631,7 +5849,22 @@ dependencies = [
"bytemuck",
"cfg-if",
"png",
"tiny-skia-path",
"tiny-skia-path 0.8.4",
]
[[package]]
name = "tiny-skia"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7db11798945fa5c3e5490c794ccca7c6de86d3afdd54b4eb324109939c6f37bc"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"png",
"tiny-skia-path 0.10.0",
]
[[package]]
@ -5645,6 +5878,17 @@ dependencies = [
"strict-num",
]
[[package]]
name = "tiny-skia-path"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f60aa35c89ac2687ace1a2556eaaea68e8c0d47408a2e3e7f5c98a489e7281c"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -5891,6 +6135,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "ttf-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
[[package]]
name = "ttf-parser"
version = "0.19.1"
@ -5970,6 +6220,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-vo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]]
name = "unicode-width"
version = "0.1.10"
@ -6000,6 +6256,127 @@ dependencies = [
"serde",
]
[[package]]
name = "usvg"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae32eb823aab35fc343b19c4d354f70e713b442ce34cdfa8497bf6c39af8a342"
dependencies = [
"base64 0.21.2",
"log",
"pico-args",
"usvg-parser 0.33.0",
"usvg-text-layout 0.33.0",
"usvg-tree 0.33.0",
"xmlwriter",
]
[[package]]
name = "usvg"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14d09ddfb0d93bf84824c09336d32e42f80961a9d1680832eb24fdf249ce11e6"
dependencies = [
"base64 0.21.2",
"log",
"pico-args",
"usvg-parser 0.35.0",
"usvg-text-layout 0.35.0",
"usvg-tree 0.35.0",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7529174e721c8078d62b08399258469b1d68b4e5f2983b347d6a9d39779366c"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"rosvgtree",
"strict-num",
"svgtypes",
"usvg-tree 0.33.0",
]
[[package]]
name = "usvg-parser"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19bf93d230813599927d88557014e0908ecc3531666d47c634c6838bc8db408"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"roxmltree",
"simplecss",
"siphasher",
"svgtypes",
"usvg-tree 0.35.0",
]
[[package]]
name = "usvg-text-layout"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e672fbc19261c6553113cc04ff2ff38ae52fadbd90f2d814040857795fb5c50"
dependencies = [
"fontdb",
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"rustybuzz 0.7.0",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"usvg-tree 0.33.0",
]
[[package]]
name = "usvg-text-layout"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "035044604e89652c0a2959b8b356946997a52649ba6cade45928c2842376feb4"
dependencies = [
"fontdb",
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"rustybuzz 0.7.0",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"usvg-tree 0.35.0",
]
[[package]]
name = "usvg-tree"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a56e9cd3be5eb6d6744477e95b82d52d393fc1dba4b5b090912c33af337c20b"
dependencies = [
"kurbo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rctree",
"strict-num",
"svgtypes",
]
[[package]]
name = "usvg-tree"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7939a7e4ed21cadb5d311d6339730681c3e24c3e81d60065be80e485d3fc8b92"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path 0.10.0",
]
[[package]]
name = "utf-8"
version = "0.7.6"
@ -6033,6 +6410,40 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "vello"
version = "0.0.1"
source = "git+https://github.com/linebender/vello#0d5a926056c06f0fc005fde5e321507758994dbb"
dependencies = [
"bytemuck",
"fello",
"futures-intrusive",
"peniko",
"raw-window-handle",
"vello_encoding",
"wgpu",
]
[[package]]
name = "vello_encoding"
version = "0.1.0"
source = "git+https://github.com/linebender/vello#0d5a926056c06f0fc005fde5e321507758994dbb"
dependencies = [
"bytemuck",
"fello",
"guillotiere",
"peniko",
]
[[package]]
name = "vello_svg"
version = "0.0.1"
source = "git+https://github.com/linebender/vello#0d5a926056c06f0fc005fde5e321507758994dbb"
dependencies = [
"usvg 0.33.0",
"vello",
]
[[package]]
name = "version-compare"
version = "0.0.11"
@ -7074,6 +7485,18 @@ version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"
[[package]]
name = "xmlparser"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zbus"
version = "3.14.1"

View File

@ -184,7 +184,7 @@ impl NodeRuntime {
resolution: viewport_resolution,
..Default::default()
},
export_format: graphene_core::application_io::ExportFormat::Svg,
export_format: graphene_core::application_io::ExportFormat::Canvas,
},
image_frame: None,
};
@ -402,7 +402,7 @@ impl NodeGraphExecutor {
use image::{ImageBuffer, Rgba};
use std::io::Cursor;
let (result_bytes, width, height) = image.into_flat_u8();
let (result_bytes, width, height) = image.to_flat_u8();
let mut output: ImageBuffer<Rgba<u8>, _> = image::ImageBuffer::from_raw(width, height, result_bytes).ok_or_else(|| "Invalid image size".to_string())?;
if let Some(size) = resize {

View File

@ -16,7 +16,7 @@ use std::fmt::{Debug, Formatter, Result};
/// Representation of the handle point(s) in a bezier segment.
#[derive(Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
enum BezierHandles {
pub enum BezierHandles {
Linear,
/// Handles for a quadratic curve.
Quadratic {
@ -42,11 +42,11 @@ unsafe impl dyn_any::StaticType for BezierHandles {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Bezier {
/// Start point of the bezier curve.
start: DVec2,
pub start: DVec2,
/// Start point of the bezier curve.
end: DVec2,
pub end: DVec2,
/// Handles of the bezier curve.
handles: BezierHandles,
pub handles: BezierHandles,
}
impl Debug for Bezier {

View File

@ -16,7 +16,7 @@ use std::ops::{Index, IndexMut};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Subpath<ManipulatorGroupId: crate::Identifier> {
manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>>,
closed: bool,
pub closed: bool,
}
#[cfg(feature = "dyn-any")]

View File

@ -9,25 +9,10 @@ license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
std = [
"dyn-any",
"dyn-any/std",
"alloc",
"glam/std",
"specta",
"num-traits/std",
"rustybuzz",
"image",
]
std = ["dyn-any", "dyn-any/std", "alloc", "glam/std", "specta", "num-traits/std", "rustybuzz", "image"]
default = ["async", "serde", "kurbo", "log", "std", "rand_chacha", "wasm"]
log = ["dep:log"]
serde = [
"dep:serde",
"glam/serde",
"bezier-rs/serde",
"bezier-rs/serde",
"base64",
]
serde = ["dep:serde", "glam/serde", "bezier-rs/serde", "bezier-rs/serde", "base64"]
gpu = ["spirv-std", "glam/bytemuck", "dyn-any", "glam/libm"]
async = ["async-trait", "alloc"]
nightly = []
@ -77,6 +62,7 @@ num-traits = { version = "0.2.15", default-features = false, features = [
wasm-bindgen = { workspace = true, optional = true }
js-sys = { version = "0.3.55", optional = true }
usvg = "0.35.0"
[dependencies.web-sys]
version = "0.3.4"

View File

@ -1,13 +1,14 @@
use crate::raster::{BlendMode, ImageFrame};
use crate::vector::VectorData;
use crate::vector::{subpath, VectorData};
use crate::{Color, Node};
use bezier_rs::BezierHandles;
use dyn_any::{DynAny, StaticType};
use node_macro::node_fn;
use core::future::Future;
use core::ops::{Deref, DerefMut};
use glam::IVec2;
use glam::{DAffine2, DVec2, IVec2, UVec2};
pub mod renderer;
@ -198,6 +199,110 @@ where
impl GraphicGroup {
pub const EMPTY: Self = Self(Vec::new());
pub fn to_usvg_tree(&self, resolution: UVec2, viewbox: [DVec2; 2]) -> usvg::Tree {
let root_node = usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default()));
let tree = usvg::Tree {
size: usvg::Size::from_wh(resolution.x as f32, resolution.y as f32).unwrap(),
view_box: usvg::ViewBox {
rect: usvg::NonZeroRect::from_ltrb(viewbox[0].x as f32, viewbox[0].y as f32, viewbox[1].x as f32, viewbox[1].y as f32).unwrap(),
aspect: usvg::AspectRatio::default(),
},
root: root_node.clone(),
};
for element in self.0.iter() {
root_node.append(element.to_usvg_node());
}
tree
}
}
impl GraphicElement {
fn to_usvg_node(&self) -> usvg::Node {
fn to_transform(transform: DAffine2) -> usvg::Transform {
let cols = transform.to_cols_array();
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
}
match &self.graphic_element_data {
GraphicElementData::VectorShape(vector_data) => {
use usvg::tiny_skia_path::PathBuilder;
let mut builder = PathBuilder::new();
let transform = to_transform(vector_data.transform);
let style = &vector_data.style;
for subpath in vector_data.subpaths.iter() {
let start = vector_data.transform.transform_point2(subpath[0].anchor);
builder.move_to(start.x as f32, start.y as f32);
for bezier in subpath.iter() {
bezier.apply_transformation(|pos| vector_data.transform.transform_point2(pos));
let end = bezier.end;
match bezier.handles {
BezierHandles::Linear => builder.line_to(end.x as f32, end.y as f32),
BezierHandles::Quadratic { handle } => builder.quad_to(handle.x as f32, handle.y as f32, end.x as f32, end.y as f32),
BezierHandles::Cubic { handle_start, handle_end } => {
builder.cubic_to(handle_start.x as f32, handle_start.y as f32, handle_end.x as f32, handle_end.y as f32, end.x as f32, end.y as f32)
}
}
}
if subpath.closed {
builder.close()
}
}
let path = builder.finish().unwrap();
let mut path = usvg::Path::new(path.into());
path.transform = transform;
// TODO: use proper style
path.fill = None;
path.stroke = Some(usvg::Stroke::default());
usvg::Node::new(usvg::NodeKind::Path(path))
}
GraphicElementData::ImageFrame(image_frame) => {
if image_frame.image.width * image_frame.image.height == 0 {
return usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default()));
}
let png = image_frame.image.to_png();
usvg::Node::new(usvg::NodeKind::Image(usvg::Image {
id: String::new(),
transform: to_transform(image_frame.transform),
visibility: usvg::Visibility::Visible,
view_box: usvg::ViewBox {
rect: usvg::NonZeroRect::from_xywh(0., 0., 1., 1.).unwrap(),
aspect: usvg::AspectRatio::default(),
},
rendering_mode: usvg::ImageRendering::OptimizeSpeed,
kind: usvg::ImageKind::PNG(png.into()),
}))
}
GraphicElementData::Text(text) => usvg::Node::new(usvg::NodeKind::Text(usvg::Text {
id: String::new(),
transform: usvg::Transform::identity(),
rendering_mode: usvg::TextRendering::OptimizeSpeed,
positions: Vec::new(),
rotate: Vec::new(),
writing_mode: usvg::WritingMode::LeftToRight,
chunks: vec![usvg::TextChunk {
text: text.clone(),
x: None,
y: None,
anchor: usvg::TextAnchor::Start,
spans: vec![],
text_flow: usvg::TextFlow::Linear,
}],
})),
GraphicElementData::GraphicGroup(group) => {
let group_element = usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default()));
for element in group.0.iter() {
group_element.append(element.to_usvg_node());
}
group_element
}
// TODO
GraphicElementData::Artboard(board) => usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default())),
}
}
}
impl core::hash::Hash for GraphicElement {

View File

@ -297,12 +297,7 @@ impl GraphicElementRendered for ImageFrame<Color> {
if image.data.is_empty() {
return;
}
let (flat_data, _, _) = image.clone().into_flat_u8();
let mut output = Vec::new();
let encoder = image::codecs::png::PngEncoder::new(&mut output);
encoder
.write_image(&flat_data, image.width, image.height, image::ColorType::Rgba8)
.expect("failed to encode image as png");
let output = image.to_png();
let preamble = "data:image/png;base64,";
let mut base64_string = String::with_capacity(preamble.len() + output.len() * 4);
base64_string.push_str(preamble);

View File

@ -134,6 +134,15 @@ impl Image<Color> {
let data = image_data.chunks_exact(4).map(|v| Color::from_rgba8_srgb(v[0], v[1], v[2], v[3])).collect();
Image { width, height, data }
}
pub fn to_png(&self) -> Vec<u8> {
use ::image::ImageEncoder;
let (data, width, height) = self.to_flat_u8();
let mut png = Vec::new();
let encoder = ::image::codecs::png::PngEncoder::new(&mut png);
encoder.write_image(&data, width, height, ::image::ColorType::Rgba8).expect("failed to encode image as png");
png
}
}
use super::*;
@ -143,9 +152,9 @@ where
<P as Alpha>::AlphaChannel: Linear,
{
/// Flattens each channel cast to a u8
pub fn into_flat_u8(self) -> (Vec<u8>, u32, u32) {
pub fn to_flat_u8(&self) -> (Vec<u8>, u32, u32) {
let Image { width, height, data } = self;
assert_eq!(data.len(), width as usize * height as usize);
assert_eq!(data.len(), *width as usize * *height as usize);
// Cache the last sRGB value we computed, speeds up fills.
let mut last_r = 0.;
@ -190,7 +199,7 @@ where
i += 4;
}
(result, width, height)
(result, *width, *height)
}
}

View File

@ -22,6 +22,8 @@ quantization = ["autoquant"]
wasm = ["wasm-bindgen", "web-sys", "js-sys"]
imaginate = ["image/png", "base64", "js-sys", "web-sys", "wasm-bindgen-futures"]
image-compare = ["dep:image-compare"]
vello = ["dep:vello", "resvg", "gpu", "dep:vello_svg"]
resvg = ["dep:resvg"]
wayland = []
[dependencies]
@ -72,6 +74,9 @@ winit = "0.28.6"
url = "2.4.0"
tokio = { version = "1.29.0", optional = true, features = ["fs", "io-std"] }
image-compare = { version = "0.3.0", optional = true }
vello = { git = "https://github.com/linebender/vello", version = "0.0.1", optional = true }
vello_svg = { git = "https://github.com/linebender/vello", version = "0.0.1", optional = true }
resvg = { version = "0.35.0", optional = true }
[dependencies.serde]
version = "1.0"

View File

@ -315,6 +315,7 @@ async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
render.format_svg(min, max);
RenderOutput::Svg(render.svg.to_string())
}
#[cfg(any(feature = "resvg", feature = "vello"))]
ExportFormat::Canvas => {
data.render_svg(&mut render, &render_params);
// TODO: reenable once we switch to full node graph
@ -326,7 +327,22 @@ async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
let canvas = &surface_handle.surface;
canvas.set_width(resolution.x);
canvas.set_height(resolution.y);
let usvg_tree = data.to_usvg_tree(resolution, [min, max]);
if let Some(exec) = editor.application_io.gpu_executor() {
todo!()
} else {
let rtree = resvg::Tree::from_usvg(&usvg_tree);
let pixmap_size = rtree.size.to_int_size();
let mut pixmap = resvg::tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
rtree.render(resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut());
let array: Clamped<&[u8]> = Clamped(pixmap.data());
let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap();
let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(array, pixmap_size.width(), pixmap_size.height()).expect("Failed to construct ImageData");
context.put_image_data(&image_data, 0.0, 0.0).unwrap();
}
/*
let preamble = "data:image/svg+xml;base64,";
let mut base64_string = String::with_capacity(preamble.len() + array.len() * 4);
base64_string.push_str(preamble);
@ -334,9 +350,9 @@ async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
let image_data = web_sys::HtmlImageElement::new().unwrap();
image_data.set_src(base64_string.as_str());
let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap();
wasm_bindgen_futures::JsFuture::from(image_data.decode()).await.unwrap();
context.draw_image_with_html_image_element(&image_data, 0.0, 0.0).unwrap();
*/
let frame = SurfaceHandleFrame {
surface_handle,
transform: DAffine2::IDENTITY,