GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
feat: rasterize_svg — public SVG-to-RGBA helper, extracted from upload_icon
Same tree-parse + scale + un-premultiply body; upload_icon keeps the
upload/cache shell. Thread-safe (fontdb is a OnceLock static) so
services may rasterize off the UI thread — cce-files' preview uses it.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/lib.rs | 58 ++++++++++++++++++++++++++++++++++------------------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/src/lib.rs b/src/lib.rs
index adbb4c0..914dd6e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -69,36 +69,46 @@ pub fn upload_icon(name: &str, px: u32) -> Option<(u32, u32, u32)> {
let loaded = (|| {
let path = format!("{}/{name}.svg", icons_dir());
let data = std::fs::read(&path).ok()?;
- let opt = resvg::usvg::Options::default();
- let fontdb = crate::widget::get_font_db();
- let tree = resvg::usvg::Tree::from_data(&data, &opt, fontdb).ok()?;
- let size = tree.size();
- let (sw, sh) = (size.width().max(1.0), size.height().max(1.0));
- let scale = px as f32 / sw.max(sh);
- let w = (sw * scale).round().max(1.0) as u32;
- let h = (sh * scale).round().max(1.0) as u32;
- let mut pixmap = resvg::tiny_skia::Pixmap::new(w, h)?;
- resvg::render(
- &tree,
- resvg::tiny_skia::Transform::from_scale(scale, scale),
- &mut pixmap.as_mut(),
- );
- // tiny-skia pixels are premultiplied; the upload path takes straight RGBA.
- let mut rgba = pixmap.take();
- for p in rgba.chunks_exact_mut(4) {
- let a = p[3] as f32 / 255.0;
- if a > 0.0 {
- p[0] = ((p[0] as f32 / a).min(255.0)) as u8;
- p[1] = ((p[1] as f32 / a).min(255.0)) as u8;
- p[2] = ((p[2] as f32 / a).min(255.0)) as u8;
- }
- }
+ let (rgba, w, h) = rasterize_svg(&data, px)?;
Some((crate::vk::upload_rgba(rgba, w, h), w, h))
})();
cache.insert(key, loaded);
loaded
}
+/// Rasterize SVG bytes at `px` on the longer side: straight (un-premultiplied)
+/// RGBA8 pixels plus dimensions, ready for `vk::upload_rgba`. Text elements
+/// resolve through the shared fontdb (a thread-safe static), so callers may
+/// rasterize off the UI thread and upload later — cce-files' preview service
+/// does. `None` when the data is unparsable.
+pub fn rasterize_svg(data: &[u8], px: u32) -> Option<(Vec<u8>, u32, u32)> {
+ let opt = resvg::usvg::Options::default();
+ let fontdb = crate::widget::get_font_db();
+ let tree = resvg::usvg::Tree::from_data(data, &opt, fontdb).ok()?;
+ let size = tree.size();
+ let (sw, sh) = (size.width().max(1.0), size.height().max(1.0));
+ let scale = px as f32 / sw.max(sh);
+ let w = (sw * scale).round().max(1.0) as u32;
+ let h = (sh * scale).round().max(1.0) as u32;
+ let mut pixmap = resvg::tiny_skia::Pixmap::new(w, h)?;
+ resvg::render(
+ &tree,
+ resvg::tiny_skia::Transform::from_scale(scale, scale),
+ &mut pixmap.as_mut(),
+ );
+ // tiny-skia pixels are premultiplied; the upload path takes straight RGBA.
+ let mut rgba = pixmap.take();
+ for p in rgba.chunks_exact_mut(4) {
+ let a = p[3] as f32 / 255.0;
+ if a > 0.0 {
+ p[0] = ((p[0] as f32 / a).min(255.0)) as u8;
+ p[1] = ((p[1] as f32 / a).min(255.0)) as u8;
+ p[2] = ((p[2] as f32 / a).min(255.0)) as u8;
+ }
+ }
+ Some((rgba, w, h))
+}
+
/// Build a glyphon `FontSystem` loaded with the bundled CCE fonts (house style).
/// System fonts are loaded only if `$CCE_LOAD_SYSTEM_FONTS` is set. Configured
/// custom fonts are validated with a warning if missing.