[librsvg/wip/aruiz/rust-pixbuf-loader] gdk-pixbuf-loader: initial attempt to port to rust




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]