trying to figure out a more performant way to do svgs
Some checks failed
/ test (push) Has been cancelled

This commit is contained in:
Chris Cochrun 2025-08-27 15:33:27 -05:00
parent 5f3d867ad7
commit 1446e35c58
5 changed files with 208 additions and 78 deletions

113
Cargo.lock generated
View file

@ -1276,7 +1276,7 @@ dependencies = [
"log", "log",
"rangemap", "rangemap",
"rustc-hash 1.1.0", "rustc-hash 1.1.0",
"rustybuzz", "rustybuzz 0.14.1",
"self_cell", "self_cell",
"smol_str", "smol_str",
"swash", "swash",
@ -2943,7 +2943,7 @@ dependencies = [
"iced_graphics", "iced_graphics",
"kurbo 0.10.4", "kurbo 0.10.4",
"log", "log",
"resvg", "resvg 0.42.0",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"softbuffer", "softbuffer",
"tiny-skia", "tiny-skia",
@ -2984,7 +2984,7 @@ dependencies = [
"lyon", "lyon",
"once_cell", "once_cell",
"raw-window-handle", "raw-window-handle",
"resvg", "resvg 0.42.0",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"rustix 0.38.44", "rustix 0.38.44",
"thiserror 1.0.69", "thiserror 1.0.69",
@ -3166,12 +3166,28 @@ dependencies = [
"zune-jpeg", "zune-jpeg",
] ]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]] [[package]]
name = "imagesize" name = "imagesize"
version = "0.12.0" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "imagesize"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
[[package]] [[package]]
name = "immutable-chunkmap" name = "immutable-chunkmap"
version = "2.0.6" version = "2.0.6"
@ -3643,6 +3659,7 @@ dependencies = [
"miette", "miette",
"pretty_assertions", "pretty_assertions",
"rayon", "rayon",
"resvg 0.45.1",
"rodio", "rodio",
"ron 0.8.1", "ron 0.8.1",
"serde", "serde",
@ -4927,6 +4944,12 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.37.5" version = "0.37.5"
@ -5200,7 +5223,24 @@ dependencies = [
"rgb", "rgb",
"svgtypes", "svgtypes",
"tiny-skia", "tiny-skia",
"usvg", "usvg 0.42.0",
]
[[package]]
name = "resvg"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
dependencies = [
"gif",
"image-webp",
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg 0.45.1",
"zune-jpeg",
] ]
[[package]] [[package]]
@ -5376,8 +5416,26 @@ dependencies = [
"libm", "libm",
"smallvec", "smallvec",
"ttf-parser 0.21.1", "ttf-parser 0.21.1",
"unicode-bidi-mirroring", "unicode-bidi-mirroring 0.2.0",
"unicode-ccc", "unicode-ccc 0.2.0",
"unicode-properties",
"unicode-script",
]
[[package]]
name = "rustybuzz"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
dependencies = [
"bitflags 2.9.2",
"bytemuck",
"core_maths",
"log",
"smallvec",
"ttf-parser 0.25.1",
"unicode-bidi-mirroring 0.4.0",
"unicode-ccc 0.4.0",
"unicode-properties", "unicode-properties",
"unicode-script", "unicode-script",
] ]
@ -6729,12 +6787,24 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
[[package]] [[package]]
name = "unicode-ccc" name = "unicode-ccc"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]]
name = "unicode-ccc"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
@ -6826,12 +6896,39 @@ dependencies = [
"data-url", "data-url",
"flate2", "flate2",
"fontdb 0.18.0", "fontdb 0.18.0",
"imagesize", "imagesize 0.12.0",
"kurbo 0.11.3", "kurbo 0.11.3",
"log", "log",
"pico-args", "pico-args",
"roxmltree", "roxmltree",
"rustybuzz", "rustybuzz 0.14.1",
"simplecss",
"siphasher",
"strict-num",
"svgtypes",
"tiny-skia-path",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]]
name = "usvg"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef"
dependencies = [
"base64 0.22.1",
"data-url",
"flate2",
"fontdb 0.23.0",
"imagesize 0.13.0",
"kurbo 0.11.3",
"log",
"pico-args",
"roxmltree",
"rustybuzz 0.20.1",
"simplecss", "simplecss",
"siphasher", "siphasher",
"strict-num", "strict-num",

View file

@ -32,7 +32,7 @@ gstreamer-app = "0.23"
url = "2" url = "2"
colors-transform = "0.2.11" colors-transform = "0.2.11"
rayon = "1.11.0" rayon = "1.11.0"
# resvg = "0.45.1" resvg = "0.45.1"
# femtovg = { version = "0.16.0", features = ["wgpu"] } # femtovg = { version = "0.16.0", features = ["wgpu"] }
# wgpu = "26.0.1" # wgpu = "26.0.1"
# mupdf = "0.5.0" # mupdf = "0.5.0"

View file

@ -6,6 +6,7 @@ use std::sync::Arc;
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use crisp::types::{Keyword, Symbol, Value}; use crisp::types::{Keyword, Symbol, Value};
use miette::Result; use miette::Result;
use resvg::usvg::fontdb;
use tracing::{debug, error}; use tracing::{debug, error};
use crate::Slide; use crate::Slide;
@ -17,7 +18,7 @@ use super::videos::Video;
use super::kinds::ServiceItemKind; use super::kinds::ServiceItemKind;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct ServiceItem { pub struct ServiceItem {
pub id: i32, pub id: i32,
pub title: String, pub title: String,

View file

@ -2,10 +2,12 @@
use crisp::types::{Keyword, Symbol, Value}; use crisp::types::{Keyword, Symbol, Value};
use iced_video_player::Video; use iced_video_player::Video;
use miette::{miette, Result}; use miette::{miette, Result};
use resvg::usvg::fontdb;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt::Display, fmt::Display,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc,
}; };
use tracing::error; use tracing::error;
@ -13,6 +15,40 @@ use crate::ui::text_svg::{self, TextSvg};
use super::songs::Song; use super::songs::Song;
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Slide {
id: i32,
background: Background,
text: String,
font: String,
font_size: i32,
text_alignment: TextAlignment,
audio: Option<PathBuf>,
video_loop: bool,
video_start_time: f32,
video_end_time: f32,
#[serde(skip)]
pub text_svg: TextSvg,
}
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum BackgroundKind {
#[default]
Image,
Video,
}
#[derive(Debug, Clone, Default)]
struct Image {
pub source: String,
pub fit: String,
pub children: Vec<String>,
}
#[derive( #[derive(
Clone, Clone,
Copy, Copy,
@ -203,15 +239,6 @@ impl Display for ParseError {
} }
} }
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum BackgroundKind {
#[default]
Image,
Video,
}
impl From<String> for BackgroundKind { impl From<String> for BackgroundKind {
fn from(value: String) -> Self { fn from(value: String) -> Self {
if value == "image" { if value == "image" {
@ -222,24 +249,6 @@ impl From<String> for BackgroundKind {
} }
} }
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Slide {
id: i32,
background: Background,
text: String,
font: String,
font_size: i32,
text_alignment: TextAlignment,
audio: Option<PathBuf>,
video_loop: bool,
video_start_time: f32,
video_end_time: f32,
#[serde(skip)]
pub text_svg: TextSvg,
}
impl From<&Slide> for Value { impl From<&Slide> for Value {
fn from(value: &Slide) -> Self { fn from(value: &Slide) -> Self {
Self::List(vec![Self::Symbol(Symbol("slide".into()))]) Self::List(vec![Self::Symbol(Symbol("slide".into()))])
@ -656,13 +665,6 @@ impl SlideBuilder {
} }
} }
#[derive(Debug, Clone, Default)]
struct Image {
pub source: String,
pub fit: String,
pub children: Vec<String>,
}
impl Image { impl Image {
fn new() -> Self { fn new() -> Self {
Self { Self {

View file

@ -1,22 +1,28 @@
use std::{ use std::{
fmt::Display, fmt::Display,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
io::Read,
sync::Arc,
}; };
use colors_transform::Rgb; use colors_transform::Rgb;
use cosmic::{ use cosmic::{
iced::{ iced::{
font::{Style, Weight}, font::{Style, Weight},
Length, Size, ContentFit, Length, Size,
}, },
prelude::*, prelude::*,
widget::{container, svg::Handle, Svg}, widget::{container, image::Handle, Image},
}; };
use tracing::error; use resvg::{
tiny_skia::{self, Pixmap},
usvg::Tree,
};
use tracing::{debug, error};
use crate::TextAlignment; use crate::TextAlignment;
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default)]
pub struct TextSvg { pub struct TextSvg {
text: String, text: String,
font: Font, font: Font,
@ -25,6 +31,19 @@ pub struct TextSvg {
fill: Color, fill: Color,
alignment: TextAlignment, alignment: TextAlignment,
handle: Option<Handle>, handle: Option<Handle>,
fontdb: Arc<resvg::usvg::fontdb::Database>,
}
impl PartialEq for TextSvg {
fn eq(&self, other: &Self) -> bool {
self.text == other.text
&& self.font == other.font
&& self.shadow == other.shadow
&& self.stroke == other.stroke
&& self.fill == other.fill
&& self.alignment == other.alignment
&& self.handle == other.handle
}
} }
impl Hash for TextSvg { impl Hash for TextSvg {
@ -46,6 +65,27 @@ pub struct Font {
size: u8, size: u8,
} }
#[derive(Clone, Debug, Default, PartialEq, Hash)]
pub struct Shadow {
pub offset_x: i16,
pub offset_y: i16,
pub spread: u16,
pub color: Color,
}
#[derive(Clone, Debug, Default, PartialEq, Hash)]
pub struct Stroke {
size: u16,
color: Color,
}
pub enum Message {
None,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Color(Rgb);
impl From<cosmic::font::Font> for Font { impl From<cosmic::font::Font> for Font {
fn from(value: cosmic::font::Font) -> Self { fn from(value: cosmic::font::Font) -> Self {
Self { Self {
@ -113,9 +153,6 @@ impl Font {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub struct Color(Rgb);
impl Hash for Color { impl Hash for Color {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_css_hex_string().hash(state); self.0.to_css_hex_string().hash(state);
@ -158,24 +195,6 @@ impl Display for Color {
} }
} }
#[derive(Clone, Debug, Default, PartialEq, Hash)]
pub struct Shadow {
pub offset_x: i16,
pub offset_y: i16,
pub spread: u16,
pub color: Color,
}
#[derive(Clone, Debug, Default, PartialEq, Hash)]
pub struct Stroke {
size: u16,
color: Color,
}
pub enum Message {
None,
}
impl TextSvg { impl TextSvg {
pub fn new(text: impl Into<String>) -> Self { pub fn new(text: impl Into<String>) -> Self {
Self { Self {
@ -234,7 +253,7 @@ impl TextSvg {
} else { } else {
"".into() "".into()
}; };
let size = Size::new(640.0, 360.0); let size = Size::new(1920.0, 1080.0);
let total_lines = self.text.lines().count(); let total_lines = self.text.lines().count();
let half_lines = (total_lines / 2) as f32; let half_lines = (total_lines / 2) as f32;
let middle_position = size.height / 2.0; let middle_position = size.height / 2.0;
@ -266,20 +285,31 @@ impl TextSvg {
self.font.name, self.font.name,
self.font.size, self.font.size,
self.fill, stroke, text); self.fill, stroke, text);
let handle = Handle::from_memory( debug!(?final_svg);
Box::leak( let resvg_tree = Tree::from_str(
<std::string::String as Clone>::clone(&final_svg) &final_svg,
.into_boxed_str(), &resvg::usvg::Options {
) fontdb: Arc::clone(&self.fontdb),
.as_bytes(), ..Default::default()
); },
)
.expect("Woops mama");
// debug!(?resvg_tree);
let transform = tiny_skia::Transform::default();
let mut pixmap =
Pixmap::new(size.width as u32, size.height as u32)
.expect("opops");
resvg::render(&resvg_tree, transform, &mut pixmap.as_mut());
// debug!(?pixmap);
let handle = Handle::from_bytes(pixmap.data().to_owned());
self.handle = Some(handle); self.handle = Some(handle);
self self
} }
pub fn view<'a>(&self) -> Element<'a, Message> { pub fn view<'a>(&self) -> Element<'a, Message> {
container( container(
Svg::new(self.handle.clone().unwrap()) Image::new(self.handle.clone().unwrap())
.content_fit(ContentFit::Contain)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill), .height(Length::Fill),
) )