From f0db30c92348b0a71633c25d25569c925d0c77b7 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 3 Mar 2023 09:03:12 +0100 Subject: cairo-renderer: add support for SVG images --- cairo-renderer/src/lib.rs | 83 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 13 deletions(-) (limited to 'cairo-renderer/src') diff --git a/cairo-renderer/src/lib.rs b/cairo-renderer/src/lib.rs index 01eb7ef..d49e5ce 100644 --- a/cairo-renderer/src/lib.rs +++ b/cairo-renderer/src/lib.rs @@ -4,6 +4,8 @@ use std::{ }; use gdk::{gdk_pixbuf::Pixbuf, prelude::GdkContextExt}; +use librsvg::SvgHandle; +use mime_guess::mime; use diaphragm_core::{ styles::{DefinedDashStyle, Pattern}, @@ -11,9 +13,38 @@ use diaphragm_core::{ Renderer, }; +enum Image { + Pixbuf(Pixbuf), + Svg(SvgHandle), +} + +impl Image { + fn width(&self) -> f64 { + match self { + Image::Pixbuf(pixbuf) => pixbuf.width() as f64, + Image::Svg(handle) => { + let dimensions = librsvg::CairoRenderer::new(handle).intrinsic_dimensions(); + // TODO: this doesn't check if the SVG has different unit for width of height + dimensions.width.length + } + } + } + + fn height(&self) -> f64 { + match self { + Image::Pixbuf(pixbuf) => pixbuf.height() as f64, + Image::Svg(handle) => { + let dimensions = librsvg::CairoRenderer::new(handle).intrinsic_dimensions(); + // TODO: this doesn't check if the SVG has different unit for width of height + dimensions.height.length + } + } + } +} + pub struct CairoRenderer { ctx: cairo::Context, - loaded_images: HashMap, + loaded_images: HashMap, } impl CairoRenderer { @@ -29,11 +60,24 @@ impl CairoRenderer { } } - fn get_image(&mut self, path: &Path) -> &Pixbuf { - let path = path.to_owned(); - self.loaded_images - .entry(path.clone()) - .or_insert_with(|| Pixbuf::from_file(path).unwrap()) + fn load_image(&mut self, path: &Path) { + if self.loaded_images.contains_key(path) { + return; + } + + let mime_type = mime_guess::from_path(&path).first().unwrap(); + assert_eq!(mime_type.type_(), "image", "File is not an image"); + let image = match mime_type.subtype() { + mime::SVG => Image::Svg(librsvg::Loader::new().read_path(path).unwrap()), + // TODO: use Pixbuf::formats() to check for supported formats + mime::PNG | mime::GIF | mime::JPEG => Image::Pixbuf(Pixbuf::from_file(path).unwrap()), + other => panic!("Unsupported image type: {}", other), + }; + self.loaded_images.insert(path.to_owned(), image); + } + + fn get_image(&self, path: &Path) -> &Image { + self.loaded_images.get(path).unwrap() } } @@ -134,23 +178,36 @@ impl Renderer for CairoRenderer { } fn show_image(&mut self, path: &Path, x: f64, y: f64, width: f64, height: f64) { - let image = self.get_image(path).clone(); + self.load_image(path); + let image = self.get_image(path); self.ctx.save().unwrap(); - let scale_x = width / image.width() as f64; - let scale_y = height / image.height() as f64; + match image { + Image::Pixbuf(pixbuf) => { + let scale_x = width / image.width(); + let scale_y = height / image.height(); - self.ctx.scale(scale_x, scale_y); + self.ctx.scale(scale_x, scale_y); - self.ctx.set_source_pixbuf(&image, x / scale_x, y / scale_y); - self.ctx.paint().unwrap(); + self.ctx.set_source_pixbuf(pixbuf, x / scale_x, y / scale_y); + self.ctx.paint().unwrap(); + } + Image::Svg(handle) => { + // TODO: if aspect ratio is not kept, the image is not "scaled", only margins are + // added + librsvg::CairoRenderer::new(handle) + .render_document(&self.ctx, &cairo::Rectangle::new(x, y, width, height)) + .unwrap(); + } + } self.ctx.restore().unwrap(); } fn geometry_for_image(&mut self, path: &Path) -> (f64, f64) { + self.load_image(path); let image = self.get_image(path); - (image.width() as f64, image.height() as f64) + (image.width(), image.height()) } } -- cgit v1.2.3