[librsvg] rsvg-pixbuf.c: Port all the functions here to Rust



commit d7ed7b51ff4d4589d71d28f368ad53ead8c86c80
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri Jan 11 19:11:12 2019 -0600

    rsvg-pixbuf.c: Port all the functions here to Rust

 Makefile.am                        |   2 -
 librsvg/rsvg-pixbuf.c              | 132 +++++++++------------
 rsvg_internals/src/handle.rs       |  18 +--
 rsvg_internals/src/lib.rs          |   7 ++
 rsvg_internals/src/pixbuf_utils.rs | 236 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 302 insertions(+), 93 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d83aaaeb..7412c9a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,8 +29,6 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
        librsvg/rsvg-css.h                      \
        librsvg/rsvg-handle.c                   \
        librsvg/rsvg-pixbuf.c                   \
-       librsvg/rsvg-size-callback.c            \
-       librsvg/rsvg-size-callback.h            \
        librsvg/rsvg.h                          \
        $(NULL)
 
diff --git a/librsvg/rsvg-pixbuf.c b/librsvg/rsvg-pixbuf.c
index adafe3ba..65cf388f 100644
--- a/librsvg/rsvg-pixbuf.c
+++ b/librsvg/rsvg-pixbuf.c
@@ -40,37 +40,33 @@
 #include <stdlib.h>
 
 #include "rsvg.h"
-#include "rsvg-size-callback.h"
 
-static GdkPixbuf *
-rsvg_pixbuf_from_file_with_size_data (const gchar * file_name,
-                                      struct RsvgSizeCallbackData *cb_data, 
-                                      GError ** error)
-{
-    GFile *file;
-    RsvgHandle *handle;
-    GdkPixbuf *pixbuf = NULL;
-
-    file = g_file_new_for_path (file_name);
-    handle = rsvg_handle_new_from_gfile_sync (file, 0, NULL, error);
-
-    if (handle) {
-        rsvg_handle_set_size_callback (handle, _rsvg_size_callback, cb_data, NULL);
-        pixbuf = rsvg_handle_get_pixbuf (handle);
-    }
-
-    g_object_unref (handle);
-    g_object_unref (file);
-
-    return pixbuf;
-}
+/* Defined in rsvg_internals/src/pixbuf_utils.rs */
+extern GdkPixbuf *rsvg_rust_pixbuf_from_file_at_size (const char *filename,
+                                                      int width,
+                                                      int height,
+                                                      GError **error);
+extern GdkPixbuf *rsvg_rust_pixbuf_from_file_at_zoom (const char *filename,
+                                                      double x_zoom,
+                                                      double y_zoom,
+                                                      GError **error);
+extern GdkPixbuf *rsvg_rust_pixbuf_from_file_at_zoom_with_max (const char *filename,
+                                                               double x_zoom,
+                                                               double y_zoom,
+                                                               int width,
+                                                               int height,
+                                                               GError **error);
+extern GdkPixbuf *rsvg_rust_pixbuf_from_file_at_max_size (const char *filename,
+                                                          int width,
+                                                          int height,
+                                                          GError **error);
 
 /**
  * rsvg_pixbuf_from_file:
- * @file_name: A file name
+ * @filename: A file name
  * @error: return location for errors
  * 
- * Loads a new #GdkPixbuf from @file_name and returns it.  The caller must
+ * Loads a new #GdkPixbuf from @filename and returns it.  The caller must
  * assume the reference to the reurned pixbuf. If an error occurred, @error is
  * set and %NULL is returned.
  * 
@@ -78,19 +74,22 @@ rsvg_pixbuf_from_file_with_size_data (const gchar * file_name,
  * Deprecated: Set up a cairo matrix and use rsvg_handle_new_from_file() + rsvg_handle_render_cairo() 
instead.
  **/
 GdkPixbuf *
-rsvg_pixbuf_from_file (const gchar * file_name, GError ** error)
+rsvg_pixbuf_from_file (const gchar *filename, GError **error)
 {
-    return rsvg_pixbuf_from_file_at_size (file_name, -1, -1, error);
+    g_return_val_if_fail (filename != NULL, NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+    return rsvg_rust_pixbuf_from_file_at_size (filename, -1, -1, error);
 }
 
 /**
  * rsvg_pixbuf_from_file_at_zoom:
- * @file_name: A file name
+ * @filename: A file name
  * @x_zoom: The horizontal zoom factor
  * @y_zoom: The vertical zoom factor
  * @error: return location for errors
  * 
- * Loads a new #GdkPixbuf from @file_name and returns it.  This pixbuf is scaled
+ * Loads a new #GdkPixbuf from @filename and returns it.  This pixbuf is scaled
  * from the size indicated by the file by a factor of @x_zoom and @y_zoom.  The
  * caller must assume the reference to the returned pixbuf. If an error
  * occurred, @error is set and %NULL is returned.
@@ -99,32 +98,26 @@ rsvg_pixbuf_from_file (const gchar * file_name, GError ** error)
  * Deprecated: Set up a cairo matrix and use rsvg_handle_new_from_file() + rsvg_handle_render_cairo() 
instead.
  **/
 GdkPixbuf *
-rsvg_pixbuf_from_file_at_zoom (const gchar * file_name,
-                               double x_zoom, double y_zoom, GError ** error)
+rsvg_pixbuf_from_file_at_zoom (const gchar *filename,
+                               double x_zoom, double y_zoom, GError **error)
 {
-    struct RsvgSizeCallbackData data;
-
-    g_return_val_if_fail (file_name != NULL, NULL);
+    g_return_val_if_fail (filename != NULL, NULL);
     g_return_val_if_fail (x_zoom > 0.0 && y_zoom > 0.0, NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-    data.type = RSVG_SIZE_ZOOM;
-    data.x_zoom = x_zoom;
-    data.y_zoom = y_zoom;
-    data.keep_aspect_ratio = FALSE;
-
-    return rsvg_pixbuf_from_file_with_size_data (file_name, &data, error);
+    return rsvg_rust_pixbuf_from_file_at_zoom (filename, x_zoom, y_zoom, error);
 }
 
 /**
  * rsvg_pixbuf_from_file_at_zoom_with_max:
- * @file_name: A file name
+ * @filename: A file name
  * @x_zoom: The horizontal zoom factor
  * @y_zoom: The vertical zoom factor
  * @max_width: The requested max width
  * @max_height: The requested max heigh
  * @error: return location for errors
  * 
- * Loads a new #GdkPixbuf from @file_name and returns it.  This pixbuf is scaled
+ * Loads a new #GdkPixbuf from @filename and returns it.  This pixbuf is scaled
  * from the size indicated by the file by a factor of @x_zoom and @y_zoom. If the
  * resulting pixbuf would be larger than max_width/max_heigh it is uniformly scaled
  * down to fit in that rectangle. The caller must assume the reference to the
@@ -134,34 +127,27 @@ rsvg_pixbuf_from_file_at_zoom (const gchar * file_name,
  * Deprecated: Set up a cairo matrix and use rsvg_handle_new_from_file() + rsvg_handle_render_cairo() 
instead.
  **/
 GdkPixbuf *
-rsvg_pixbuf_from_file_at_zoom_with_max (const gchar * file_name,
+rsvg_pixbuf_from_file_at_zoom_with_max (const gchar *filename,
                                         double x_zoom,
                                         double y_zoom,
-                                        gint max_width, gint max_height, GError ** error)
+                                        gint max_width, gint max_height, GError **error)
 {
-    struct RsvgSizeCallbackData data;
-
-    g_return_val_if_fail (file_name != NULL, NULL);
+    g_return_val_if_fail (filename != NULL, NULL);
     g_return_val_if_fail (x_zoom > 0.0 && y_zoom > 0.0, NULL);
+    g_return_val_if_fail (max_width >= 1 && max_height >= 1, NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-    data.type = RSVG_SIZE_ZOOM_MAX;
-    data.x_zoom = x_zoom;
-    data.y_zoom = y_zoom;
-    data.width = max_width;
-    data.height = max_height;
-    data.keep_aspect_ratio = FALSE;
-
-    return rsvg_pixbuf_from_file_with_size_data (file_name, &data, error);
+    return rsvg_rust_pixbuf_from_file_at_zoom_with_max (filename, x_zoom, y_zoom, max_width, max_height, 
error);
 }
 
 /**
  * rsvg_pixbuf_from_file_at_size:
- * @file_name: A file name
+ * @filename: A file name
  * @width: The new width, or -1
  * @height: The new height, or -1
  * @error: return location for errors
  * 
- * Loads a new #GdkPixbuf from @file_name and returns it.  This pixbuf is scaled
+ * Loads a new #GdkPixbuf from @filename and returns it.  This pixbuf is scaled
  * from the size indicated to the new size indicated by @width and @height.  If
  * either of these are -1, then the default size of the image being loaded is
  * used.  The caller must assume the reference to the returned pixbuf. If an
@@ -171,26 +157,23 @@ rsvg_pixbuf_from_file_at_zoom_with_max (const gchar * file_name,
  * Deprecated: Set up a cairo matrix and use rsvg_handle_new_from_file() + rsvg_handle_render_cairo() 
instead.
  **/
 GdkPixbuf *
-rsvg_pixbuf_from_file_at_size (const gchar * file_name, gint width, gint height, GError ** error)
+rsvg_pixbuf_from_file_at_size (const gchar *filename, gint width, gint height, GError **error)
 {
-    struct RsvgSizeCallbackData data;
-
-    data.type = RSVG_SIZE_WH;
-    data.width = width;
-    data.height = height;
-    data.keep_aspect_ratio = FALSE;
+    g_return_val_if_fail (filename != NULL, NULL);
+    g_return_val_if_fail ((width >= 1 && height >= 1) || (width == -1 && height == -1), NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-    return rsvg_pixbuf_from_file_with_size_data (file_name, &data, error);
+    return rsvg_rust_pixbuf_from_file_at_size (filename, width, height, error);
 }
 
 /**
  * rsvg_pixbuf_from_file_at_max_size:
- * @file_name: A file name
+ * @filename: A file name
  * @max_width: The requested max width
  * @max_height: The requested max heigh
  * @error: return location for errors
  * 
- * Loads a new #GdkPixbuf from @file_name and returns it.  This pixbuf is uniformly
+ * Loads a new #GdkPixbuf from @filename and returns it.  This pixbuf is uniformly
  * scaled so that the it fits into a rectangle of size max_width * max_height. The
  * caller must assume the reference to the returned pixbuf. If an error occurred,
  * @error is set and %NULL is returned.
@@ -199,15 +182,12 @@ rsvg_pixbuf_from_file_at_size (const gchar * file_name, gint width, gint height,
  * Deprecated: Set up a cairo matrix and use rsvg_handle_new_from_file() + rsvg_handle_render_cairo() 
instead.
  **/
 GdkPixbuf *
-rsvg_pixbuf_from_file_at_max_size (const gchar * file_name,
-                                   gint max_width, gint max_height, GError ** error)
+rsvg_pixbuf_from_file_at_max_size (const gchar *filename,
+                                   gint max_width, gint max_height, GError **error)
 {
-    struct RsvgSizeCallbackData data;
-
-    data.type = RSVG_SIZE_WH_MAX;
-    data.width = max_width;
-    data.height = max_height;
-    data.keep_aspect_ratio = FALSE;
+    g_return_val_if_fail (filename != NULL, NULL);
+    g_return_val_if_fail (max_width >= 1 && max_height >= 1, NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-    return rsvg_pixbuf_from_file_with_size_data (file_name, &data, error);
+    return rsvg_rust_pixbuf_from_file_at_max_size(filename, max_width, max_height, error);
 }
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index 999c8833..0af787b3 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -42,17 +42,17 @@ pub struct RsvgHandle {
 // Keep in sync with rsvg.h:RsvgDimensionData
 #[repr(C)]
 pub struct RsvgDimensionData {
-    width: libc::c_int,
-    height: libc::c_int,
-    em: f64,
-    ex: f64,
+    pub width: libc::c_int,
+    pub height: libc::c_int,
+    pub em: f64,
+    pub ex: f64,
 }
 
 // Keep in sync with rsvg.h:RsvgPositionData
 #[repr(C)]
 pub struct RsvgPositionData {
-    x: libc::c_int,
-    y: libc::c_int,
+    pub x: libc::c_int,
+    pub y: libc::c_int,
 }
 
 /// Flags used during loading
@@ -319,7 +319,7 @@ impl Handle {
         draw_ctx
     }
 
-    fn get_dimensions(&mut self) -> Result<RsvgDimensionData, RenderingError> {
+    pub fn get_dimensions(&mut self) -> Result<RsvgDimensionData, RenderingError> {
         // This function is probably called from the cairo_render functions,
         // or is being erroneously called within the size_func.
         // To prevent an infinite loop we are saving the state, and
@@ -499,7 +499,7 @@ impl Handle {
         self.lookup_node(name).is_ok()
     }
 
-    fn render_cairo_sub(
+    pub fn render_cairo_sub(
         &mut self,
         cr: &cairo::Context,
         id: Option<&str>,
@@ -717,7 +717,7 @@ pub unsafe extern "C" fn rsvg_handle_rust_free(raw_handle: *mut Handle) {
     Box::from_raw(raw_handle);
 }
 
-fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a mut Handle {
+pub fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a mut Handle {
     unsafe { &mut *(rsvg_handle_get_rust(handle) as *mut Handle) }
 }
 
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index ed906bb4..be39ff8d 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -69,6 +69,13 @@ pub use handle::{
     rsvg_handle_rust_write,
 };
 
+pub use pixbuf_utils::{
+    rsvg_rust_pixbuf_from_file_at_max_size,
+    rsvg_rust_pixbuf_from_file_at_size,
+    rsvg_rust_pixbuf_from_file_at_zoom,
+    rsvg_rust_pixbuf_from_file_at_zoom_with_max,
+};
+
 pub use xml::rsvg_xml_state_error;
 
 #[macro_use]
diff --git a/rsvg_internals/src/pixbuf_utils.rs b/rsvg_internals/src/pixbuf_utils.rs
index a809518a..bfdd6dbf 100644
--- a/rsvg_internals/src/pixbuf_utils.rs
+++ b/rsvg_internals/src/pixbuf_utils.rs
@@ -1,17 +1,32 @@
-use glib::translate::*;
+use std::ptr;
 
-use error::RenderingError;
+use cairo::{self, ImageSurface};
 use gdk_pixbuf::{Colorspace, Pixbuf};
-use gdk_pixbuf_sys as ffi;
+use gdk_pixbuf_sys;
+use glib::translate::*;
+use glib_sys;
+use libc;
+
+use error::{set_gerror, RenderingError};
+use handle::{get_rust_handle, rsvg_handle_rust_new_from_gfile_sync, Handle, RsvgDimensionData};
 use rect::IRect;
-use surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface};
+use surface_utils::{
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    shared_surface::SurfaceType,
+};
 
 // Pixbuf::new() doesn't return out-of-memory errors properly
 // See https://github.com/gtk-rs/gdk-pixbuf/issues/96
 fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
     unsafe {
-        let raw_pixbuf =
-            ffi::gdk_pixbuf_new(Colorspace::Rgb.to_glib(), true.to_glib(), 8, width, height);
+        let raw_pixbuf = gdk_pixbuf_sys::gdk_pixbuf_new(
+            Colorspace::Rgb.to_glib(),
+            true.to_glib(),
+            8,
+            width,
+            height,
+        );
 
         if raw_pixbuf.is_null() {
             return Err(RenderingError::OutOfMemory);
@@ -47,3 +62,212 @@ pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, Rende
 
     Ok(pixbuf)
 }
+
+enum SizeKind {
+    Zoom,
+    WidthHeight,
+    WidthHeightMax,
+    ZoomMax,
+}
+
+struct SizeMode {
+    kind: SizeKind,
+    x_zoom: f64,
+    y_zoom: f64,
+    width: i32,
+    height: i32,
+}
+
+fn get_final_size(dimensions: &RsvgDimensionData, size_mode: &SizeMode) -> (i32, i32) {
+    let in_width = dimensions.width;
+    let in_height = dimensions.height;
+
+    let mut out_width;
+    let mut out_height;
+
+    match size_mode.kind {
+        SizeKind::Zoom => {
+            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
+            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
+        }
+
+        SizeKind::ZoomMax => {
+            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
+            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
+
+            if out_width > size_mode.width || out_height > size_mode.height {
+                let zoom_x = f64::from(size_mode.width) / f64::from(out_width);
+                let zoom_y = f64::from(size_mode.height) / f64::from(out_height);
+                let zoom = zoom_x.min(zoom_y);
+
+                out_width = (zoom * f64::from(out_width) + 0.5) as i32;
+                out_height = (zoom * f64::from(out_height) + 0.5) as i32;
+            }
+        }
+
+        SizeKind::WidthHeightMax => {
+            let zoom_x = f64::from(size_mode.width) / f64::from(in_width);
+            let zoom_y = f64::from(size_mode.height) / f64::from(in_height);
+
+            let zoom = zoom_x.min(zoom_y);
+
+            out_width = (zoom * f64::from(in_width) + 0.5) as i32;
+            out_height = (zoom * f64::from(in_height) + 0.5) as i32;
+        }
+
+        SizeKind::WidthHeight => {
+            if size_mode.width != -1 {
+                out_width = size_mode.width;
+            } else {
+                out_width = in_width;
+            }
+
+            if size_mode.height != -1 {
+                out_height = size_mode.height;
+            } else {
+                out_height = in_height;
+            }
+        }
+    }
+
+    (out_width, out_height)
+}
+
+fn render_to_pixbuf_at_size(
+    handle: &mut Handle,
+    dimensions: &RsvgDimensionData,
+    width: i32,
+    height: i32,
+) -> Result<Pixbuf, RenderingError> {
+    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;
+
+    {
+        let cr = cairo::Context::new(&surface);
+        cr.scale(
+            f64::from(width) / f64::from(dimensions.width),
+            f64::from(height) / f64::from(dimensions.height),
+        );
+        handle.render_cairo_sub(&cr, None)?;
+    }
+
+    let shared_surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;
+
+    pixbuf_from_surface(&shared_surface)
+}
+
+fn pixbuf_from_file_with_size_mode(
+    filename: *const libc::c_char,
+    size_mode: &SizeMode,
+    error: *mut *mut glib_sys::GError,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    unsafe {
+        let file = gio_sys::g_file_new_for_path(filename);
+
+        let handle = rsvg_handle_rust_new_from_gfile_sync(file, 0, ptr::null_mut(), error);
+
+        gobject_sys::g_object_unref(file as *mut _);
+
+        if handle.is_null() {
+            return ptr::null_mut();
+        }
+
+        let rhandle = get_rust_handle(handle);
+
+        let raw_pixbuf = rhandle
+            .get_dimensions()
+            .and_then(|dimensions| {
+                let (width, height) = get_final_size(&dimensions, size_mode);
+
+                render_to_pixbuf_at_size(rhandle, &dimensions, width, height)
+            })
+            .and_then(|pixbuf| Ok(pixbuf.to_glib_full()))
+            .map_err(|e| set_gerror(error, 0, &format!("{}", e)))
+            .unwrap_or(ptr::null_mut());
+
+        gobject_sys::g_object_unref(handle as *mut _);
+
+        raw_pixbuf
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
+    filename: *const libc::c_char,
+    width: i32,
+    height: i32,
+    error: *mut *mut glib_sys::GError,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    pixbuf_from_file_with_size_mode(
+        filename,
+        &SizeMode {
+            kind: SizeKind::WidthHeight,
+            x_zoom: 0.0,
+            y_zoom: 0.0,
+            width,
+            height,
+        },
+        error,
+    )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
+    filename: *const libc::c_char,
+    x_zoom: f64,
+    y_zoom: f64,
+    error: *mut *mut glib_sys::GError,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    pixbuf_from_file_with_size_mode(
+        filename,
+        &SizeMode {
+            kind: SizeKind::Zoom,
+            x_zoom,
+            y_zoom,
+            width: 0,
+            height: 0,
+        },
+        error,
+    )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
+    filename: *const libc::c_char,
+    x_zoom: f64,
+    y_zoom: f64,
+    width: i32,
+    height: i32,
+    error: *mut *mut glib_sys::GError,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    pixbuf_from_file_with_size_mode(
+        filename,
+        &SizeMode {
+            kind: SizeKind::ZoomMax,
+            x_zoom,
+            y_zoom,
+            width,
+            height,
+        },
+        error,
+    )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
+    filename: *const libc::c_char,
+    width: i32,
+    height: i32,
+    error: *mut *mut glib_sys::GError,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    pixbuf_from_file_with_size_mode(
+        filename,
+        &SizeMode {
+            kind: SizeKind::WidthHeightMax,
+            x_zoom: 0.0,
+            y_zoom: 0.0,
+            width,
+            height,
+        },
+        error,
+    )
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]