[librsvg/wip/aruiz/rust-pixbuf-loader] gdk-pixbuf-loader: initial attempt to port to rust
- From: Alberto Ruiz <aruiz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/wip/aruiz/rust-pixbuf-loader] gdk-pixbuf-loader: initial attempt to port to rust
- Date: Thu, 28 Jul 2022 16:20:10 +0000 (UTC)
commit 053a6c036743b1d70e0ddc825c61d5d2cab5553d
Author: Alberto Ruiz <crralb amazon com>
Date: Sat Oct 24 05:09:07 2020 +0200
gdk-pixbuf-loader: initial attempt to port to rust
gdk-pixbuf-loader/Cargo.toml | 19 ++++
gdk-pixbuf-loader/src/lib.rs | 241 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 260 insertions(+)
---
diff --git a/gdk-pixbuf-loader/Cargo.toml b/gdk-pixbuf-loader/Cargo.toml
new file mode 100644
index 000000000..68ac323d3
--- /dev/null
+++ b/gdk-pixbuf-loader/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "pixbufloader-svg"
+version = "0.0.1"
+authors = ["Alberto Ruiz <aruiz gnome org>"]
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+librsvg = { path = ".." }
+gdk-pixbuf-sys = "0.15"
+gdk-pixbuf = "0.15"
+libc = "0.2"
+glib-sys = "0.15"
+gobject-sys = "0.15"
+glib = "0.15"
+gio = "0.15"
+cairo-rs = "0.15"
\ No newline at end of file
diff --git a/gdk-pixbuf-loader/src/lib.rs b/gdk-pixbuf-loader/src/lib.rs
new file mode 100644
index 000000000..2ef52bdee
--- /dev/null
+++ b/gdk-pixbuf-loader/src/lib.rs
@@ -0,0 +1,241 @@
+use std::ptr::null_mut;
+
+use gdk_pixbuf_sys::{
+ GdkPixbufFormat, GdkPixbufModule, GdkPixbufModulePattern, GdkPixbufModulePreparedFunc,
+ GdkPixbufModuleSizeFunc, GdkPixbufModuleUpdatedFunc, GDK_PIXBUF_FORMAT_SCALABLE,
+ GDK_PIXBUF_FORMAT_THREADSAFE,
+};
+
+use glib_sys::GError;
+use gobject_sys::GObject;
+use libc::{c_char, c_int, c_uint, c_void};
+
+use gio::prelude::MemoryInputStreamExt;
+use gio::MemoryInputStream;
+use glib::Bytes;
+use librsvg::Loader;
+
+#[allow(non_camel_case_types)]
+type c_bool = c_int;
+
+struct SvgContext {
+ size_func: GdkPixbufModuleSizeFunc,
+ prep_func: GdkPixbufModulePreparedFunc,
+ update_func: GdkPixbufModuleUpdatedFunc,
+ user_data: *mut c_void,
+ stream: MemoryInputStream,
+}
+
+#[no_mangle]
+unsafe extern "C" fn begin_load(
+ size_func: GdkPixbufModuleSizeFunc,
+ prep_func: GdkPixbufModulePreparedFunc,
+ update_func: GdkPixbufModuleUpdatedFunc,
+ user_data: *mut c_void,
+ error: *mut *mut GError,
+) -> *mut c_void {
+ if error != null_mut() {
+ *error = null_mut();
+ }
+
+ let stream = MemoryInputStream::new();
+ let ctx = Box::new(SvgContext {
+ size_func,
+ prep_func,
+ update_func,
+ user_data,
+ stream,
+ });
+
+ Box::into_raw(ctx) as *mut c_void
+}
+
+#[no_mangle]
+unsafe extern "C" fn load_increment(
+ user_data: *mut c_void,
+ buffer: *const u8,
+ size: c_uint,
+ error: *mut *mut GError,
+) -> c_bool {
+ if error != null_mut() {
+ *error = null_mut();
+ }
+
+ let ctx = user_data as *mut SvgContext;
+
+ let data = std::slice::from_raw_parts(buffer, size as usize);
+ let data = data.to_vec();
+ (&*ctx).stream.add_bytes(&Bytes::from_owned(data));
+ 1
+}
+
+fn argb_to_rgba(data: &mut Vec<u8>, width: usize, height: usize, stride: usize) {
+ assert!((width * 4) >= stride);
+ assert!((stride * height) <= data.len());
+ for i in 0..height {
+ let row_index = i * stride;
+ for j in 0..width {
+ let pixel_index = row_index + (j * 4);
+ let tmp = data[pixel_index + 2];
+ data[pixel_index + 2] = data[pixel_index];
+ data[pixel_index] = tmp;
+ }
+ }
+}
+
+#[no_mangle]
+unsafe extern "C" fn stop_load(user_data: *mut c_void, error: *mut *mut GError) -> c_int {
+ let ctx = Box::from_raw(user_data as *mut SvgContext);
+ if error != null_mut() {
+ *error = null_mut();
+ }
+
+ fn _inner_stop_load(ctx: &Box<SvgContext>) -> Result<(Vec<u8>, i32, i32, i32), String> {
+ let handle = Loader::new()
+ .read_stream::<_, gio::File, gio::Cancellable>(&ctx.stream, None, None)
+ .map_err(|e| e.to_string())?;
+
+ let renderer = librsvg::CairoRenderer::new(&handle);
+ let (w, h) = match renderer.intrinsic_size_in_pixels() {
+ Some((w, h)) => (w, h),
+ None => {
+ return Err(String::from(
+ "Could not get intrinsic size in pixel of Cairo memory surface",
+ ));
+ }
+ };
+
+ let surface =
+ cairo::ImageSurface::create(cairo::Format::ARgb32, w.ceil() as i32, h.ceil() as i32)
+ .map_err(|e| e.to_string())?;
+
+ let cr = cairo::Context::new(&surface).map_err(|e| e.to_string())?;
+
+ renderer
+ .render_document(
+ &cr,
+ &cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: w,
+ height: h,
+ },
+ )
+ .map_err(|e| e.to_string())?;
+
+ let w = w.ceil() as i32;
+ let h = h.ceil() as i32;
+
+ let stride = surface.stride();
+ // The cairo::Context holds a reference to the surface which needs to be dropped to access the data
+ std::mem::drop(cr);
+ let sfc_data = surface.take_data().map_err(|e| e.to_string())?;
+ let sfc_data = unsafe { std::slice::from_raw_parts(sfc_data.as_ptr(), sfc_data.len()) }; // This is
just a slice to the canonical data
+ // We need
it as a mutable vector to move the alpha channel around
+ let pb_data = sfc_data.to_vec();
+
+ Ok((pb_data, w, h, stride))
+ }
+
+ let (mut pb_data, mut w, mut h, stride) = match _inner_stop_load(&ctx) {
+ Ok(r) => r,
+ Err(e) => {
+ let gerr = glib::Error::new(gdk_pixbuf::PixbufError::Failed, &e.to_string());
+ *error = gerr.into_raw();
+ return 0;
+ }
+ };
+
+ // GDK Pixbuf only support RGBA and Cairo only ARGB32, we swap channels around
+ argb_to_rgba(&mut pb_data, w as usize, h as usize, stride as usize);
+
+ // Vector length and capacity to rebuild and destroy the vector in destroy_fn
+ let cb_data = Box::new((pb_data.len(), pb_data.capacity()));
+
+ // Function to free the pixel data by rebuilding the Vec object
+ #[no_mangle]
+ unsafe extern "C" fn destroy_cb_foo(pixels: *mut u8, user_data: *mut c_void) {
+ let data = Box::<(usize, usize)>::from_raw(user_data as *mut (usize, usize));
+ Vec::from_raw_parts(pixels, data.0, data.1);
+ }
+
+ let pb_data = pb_data.leak::<'static>(); // Allocator stops tracking vector data
+ let pixbuf = gdk_pixbuf_sys::gdk_pixbuf_new_from_data(
+ pb_data.as_mut_ptr(),
+ gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
+ 1,
+ 8,
+ w,
+ h,
+ stride,
+ Some(destroy_cb_foo),
+ Box::into_raw(cb_data) as *mut c_void,
+ );
+ if let Some(size_func) = ctx.size_func {
+ size_func(&mut w as *mut i32, &mut h as *mut i32, ctx.user_data);
+ }
+ if let Some(update_func) = ctx.update_func {
+ update_func(pixbuf, 0, 0, w, h, ctx.user_data);
+ }
+ if let Some(prep_func) = ctx.prep_func {
+ prep_func(pixbuf, null_mut(), ctx.user_data);
+ }
+
+ // The module loader increases a ref so we drop the pixbuf here
+ gobject_sys::g_object_unref(pixbuf as *mut GObject);
+
+ 1
+}
+
+#[no_mangle]
+extern "C" fn fill_vtable(module: &mut GdkPixbufModule) {
+ module.begin_load = Some(begin_load);
+ module.stop_load = Some(stop_load);
+ module.load_increment = Some(load_increment);
+}
+
+const SIGNATURE: [GdkPixbufModulePattern; 3] = [
+ GdkPixbufModulePattern {
+ prefix: " <svg\0".as_ptr() as *mut c_char,
+ mask: "* \0".as_ptr() as *mut c_char,
+ relevance: 100,
+ },
+ GdkPixbufModulePattern {
+ prefix: " <!DOCTYPE svg\0".as_ptr() as *mut c_char,
+ mask: "* \0".as_ptr() as *mut c_char,
+ relevance: 100,
+ },
+ GdkPixbufModulePattern {
+ prefix: std::ptr::null_mut(),
+ mask: std::ptr::null_mut(),
+ relevance: 0,
+ },
+];
+
+const MIME_TYPES: [*const u8; 7] = [
+ "image/svg+xml\0".as_ptr(),
+ "image/svg\0".as_ptr(),
+ "image/svg-xml\0".as_ptr(),
+ "image/vnd.adobe.svg+xml\0".as_ptr(),
+ "text/xml-svg\0".as_ptr(),
+ "image/svg+xml-compressed\0".as_ptr(),
+ std::ptr::null(),
+];
+
+const EXTENSIONS: [*const u8; 4] = [
+ "svg\0".as_ptr(),
+ "svgz\0".as_ptr(),
+ "svg.gz\0".as_ptr(),
+ std::ptr::null(),
+];
+
+#[no_mangle]
+extern "C" fn fill_info(info: &mut GdkPixbufFormat) {
+ info.name = "svg\0".as_ptr() as *mut c_char;
+ info.signature = SIGNATURE.as_ptr() as *mut GdkPixbufModulePattern;
+ info.description = "Scalable Vector Graphics\0".as_ptr() as *mut c_char; //TODO: Gettext this
+ info.mime_types = MIME_TYPES.as_ptr() as *mut *mut c_char;
+ info.extensions = EXTENSIONS.as_ptr() as *mut *mut c_char;
+ info.flags = GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
+ info.license = "LGPL\0".as_ptr() as *mut c_char;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]