[gtk+/rendering-cleanup: 34/113] API: Add gdk_pixbuf_get_from_surface()



commit a7e9b9da48937a15f37b7b6f37dfe3935ca247e1
Author: Benjamin Otte <otte redhat com>
Date:   Tue Jul 13 19:44:41 2010 +0200

    API: Add gdk_pixbuf_get_from_surface()
    
    New API to copy any cairo surface into a pixbuf.

 docs/reference/gdk/gdk3-sections.txt |    1 +
 gdk/gdk.symbols                      |    1 +
 gdk/gdkpixbuf-drawable.c             |  227 ++++++++++++++++++++++++++++++++++
 gdk/gdkpixbuf.h                      |    9 ++
 4 files changed, 238 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index 4c203c9..327f673 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -319,6 +319,7 @@ gdk_pixbuf_render_pixmap_and_mask
 gdk_pixbuf_render_pixmap_and_mask_for_colormap
 gdk_pixbuf_get_from_drawable
 gdk_pixbuf_get_from_image
+gdk_pixbuf_get_from_surface
 </SECTION>
 
 <SECTION>
diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
index 9fb237c..44c732d 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -939,6 +939,7 @@ gdk_pango_renderer_set_stipple
 #if IN_FILE(__GDK_PIXBUF_DRAWABLE_C__)
 gdk_pixbuf_get_from_drawable
 gdk_pixbuf_get_from_image
+gdk_pixbuf_get_from_surface
 #endif
 #endif
 
diff --git a/gdk/gdkpixbuf-drawable.c b/gdk/gdkpixbuf-drawable.c
index 470d450..90d204b 100644
--- a/gdk/gdkpixbuf-drawable.c
+++ b/gdk/gdkpixbuf-drawable.c
@@ -1413,3 +1413,230 @@ gdk_pixbuf_get_from_image (GdkPixbuf   *dest,
   
   return dest;
 }
+
+static cairo_format_t
+gdk_cairo_format_for_content (cairo_content_t content)
+{
+  switch (content)
+    {
+    case CAIRO_CONTENT_COLOR:
+      return CAIRO_FORMAT_RGB24;
+    case CAIRO_CONTENT_ALPHA:
+      return CAIRO_FORMAT_A8;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+    default:
+      return CAIRO_FORMAT_ARGB32;
+    }
+}
+
+static cairo_surface_t *
+gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
+                                   cairo_content_t content,
+                                   int width,
+                                   int height)
+{
+  cairo_surface_t *copy;
+  cairo_t *cr;
+
+  if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE &&
+      cairo_surface_get_content (surface) == content &&
+      cairo_image_surface_get_width (surface) >= width &&
+      cairo_image_surface_get_height (surface) >= height)
+    return cairo_surface_reference (surface);
+
+  copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
+                                     width,
+                                     height);
+
+  cr = cairo_create (copy);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_set_source_surface (cr, surface, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  return copy;
+}
+
+static void
+convert_alpha (guchar  *dest_data,
+               int      dest_stride,
+               guchar  *src_data,
+               int      src_stride,
+               int      src_x,
+               int      src_y,
+               int      dest_x,
+               int      dest_y,
+               int      width,
+               int      height)
+{
+  int x, y;
+
+  dest_data += dest_stride * dest_y + dest_x * 4;
+  src_data += src_stride * src_y + src_x * 4;
+
+  for (y = 0; y < height; y++) {
+    guint32 *src = (guint32 *) src_data;
+
+    for (x = 0; x < width; x++) {
+      guint alpha = src[x] >> 24;
+
+      if (alpha == 0)
+        {
+          dest_data[x * 4 + 0] = 0;
+          dest_data[x * 4 + 1] = 0;
+          dest_data[x * 4 + 2] = 0;
+        }
+      else
+        {
+          dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+          dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
+          dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
+        }
+      dest_data[x * 4 + 3] = alpha;
+    }
+
+    src_data += src_stride;
+    dest_data += dest_stride;
+  }
+}
+
+static void
+convert_no_alpha (guchar  *dest_data,
+                  int      dest_stride,
+                  guchar  *src_data,
+                  int      src_stride,
+                  int      src_x,
+                  int      src_y,
+                  int      dest_x,
+                  int      dest_y,
+                  int      width,
+                  int      height)
+{
+  int x, y;
+
+  dest_data += dest_stride * dest_y + dest_x * 3;
+  src_data += src_stride * src_y + src_x * 4;
+
+  for (y = 0; y < height; y++) {
+    guint32 *src = (guint32 *) src_data;
+
+    for (x = 0; x < width; x++) {
+      dest_data[x * 3 + 0] = src[x] >> 16;
+      dest_data[x * 3 + 1] = src[x] >>  8;
+      dest_data[x * 3 + 2] = src[x];
+    }
+
+    src_data += src_stride;
+    dest_data += dest_stride;
+  }
+}
+
+/**
+ * gdk_pixbuf_get_from_surface:
+ * @dest: (allow-none): Destination pixbuf, or %NULL if a new pixbuf should be created.
+ * @surface: surface to copy from
+ * @src_x: Source X coordinate within drawable.
+ * @src_y: Source Y coordinate within drawable.
+ * @dest_x: Destination X coordinate in pixbuf, or 0 if @dest is NULL.
+ * @dest_y: Destination Y coordinate in pixbuf, or 0 if @dest is NULL.
+ * @width: Width in pixels of region to get.
+ * @height: Height in pixels of region to get.
+ *
+ * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
+ * representation inside a #GdkPixbuf. This allows you to efficiently read individual
+ * pixels from Cairo surfaces. For #GdkWindows, use gdk_pixbuf_get_from_drawable()
+ * instead.
+ * 
+ * If the specified destination pixbuf @dest is %NULL, then this
+ * function will create an RGB pixbuf with 8 bits per channel. The pixbuf will
+ * contain an alpha channel if the @surface contains one. In this case, the @dest_x 
+ * and @dest_y arguments must be specified as 0.
+ *
+ * If the specified drawable is a window, and the window is off the
+ * screen, then there is no image data in the obscured/offscreen
+ * regions to be placed in the pixbuf. The contents of portions of the
+ * pixbuf corresponding to the offscreen region are undefined.
+ *
+ * If the window you're obtaining data from is partially obscured by
+ * other windows, then the contents of the pixbuf areas corresponding
+ * to the obscured regions are undefined.
+ * 
+ * If memory can't be allocated for the return value, %NULL will be returned
+ * instead.
+ *
+ * (In short, there are several ways this function can fail, and if it fails
+ *  it returns %NULL; so check the return value.)
+ *
+ * Return value: The same pixbuf as @dest if it was non-%NULL, or a newly-created
+ * pixbuf with a reference count of 1 if no destination pixbuf was specified, or %NULL on error
+ **/
+GdkPixbuf *
+gdk_pixbuf_get_from_surface  (GdkPixbuf       *dest,
+                              cairo_surface_t *surface,
+                              int              src_x,
+                              int              src_y,
+                              int              dest_x,
+                              int              dest_y,
+                              int              width,
+                              int              height)
+{
+  cairo_content_t content;
+  
+  /* General sanity checks */
+  g_return_val_if_fail (surface != NULL, NULL);
+  g_return_val_if_fail (src_x >= 0 && src_y >= 0, NULL);
+  g_return_val_if_fail (width > 0 && height > 0, NULL);
+
+  if (!dest)
+    {
+      g_return_val_if_fail (dest_x == 0 && dest_y == 0, NULL);
+      
+      content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+      dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
+                             !!(content & CAIRO_CONTENT_ALPHA),
+                             8,
+                             width, height);
+    }
+  else
+    {
+      g_return_val_if_fail (gdk_pixbuf_get_colorspace (dest) == GDK_COLORSPACE_RGB, NULL);
+      g_return_val_if_fail (gdk_pixbuf_get_n_channels (dest) == 3 ||
+                            gdk_pixbuf_get_n_channels (dest) == 4, NULL);
+      g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (dest) == 8, NULL);
+      g_return_val_if_fail (dest_x >= 0 && dest_y >= 0, NULL);
+      g_return_val_if_fail (dest_x + width <= gdk_pixbuf_get_width (dest), NULL);
+      g_return_val_if_fail (dest_y + height <= gdk_pixbuf_get_height (dest), NULL);
+
+      content = gdk_pixbuf_get_has_alpha (dest) ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR;
+    }
+
+  surface = gdk_cairo_surface_coerce_to_image (surface, content, src_x + width, src_y + height);
+  cairo_surface_flush (surface);
+  if (cairo_surface_status (surface) || dest == NULL)
+    {
+      cairo_surface_destroy (surface);
+      return NULL;
+    }
+
+  if (gdk_pixbuf_get_has_alpha (dest))
+    convert_alpha (gdk_pixbuf_get_pixels (dest),
+                   gdk_pixbuf_get_rowstride (dest),
+                   cairo_image_surface_get_data (surface),
+                   cairo_image_surface_get_stride (surface),
+                   src_x, src_y,
+                   dest_x, dest_y,
+                   width, height);
+  else
+    convert_no_alpha (gdk_pixbuf_get_pixels (dest),
+                      gdk_pixbuf_get_rowstride (dest),
+                      cairo_image_surface_get_data (surface),
+                      cairo_image_surface_get_stride (surface),
+                      src_x, src_y,
+                      dest_x, dest_y,
+                      width, height);
+
+  cairo_surface_write_to_png (surface, "foo.png");
+  cairo_surface_destroy (surface);
+  return dest;
+}
+
diff --git a/gdk/gdkpixbuf.h b/gdk/gdkpixbuf.h
index 830d6eb..b599c0b 100644
--- a/gdk/gdkpixbuf.h
+++ b/gdk/gdkpixbuf.h
@@ -72,6 +72,15 @@ GdkPixbuf *gdk_pixbuf_get_from_drawable (GdkPixbuf   *dest,
 					 int          width,
 					 int          height);
 
+GdkPixbuf *gdk_pixbuf_get_from_surface  (GdkPixbuf       *dest,
+                                         cairo_surface_t *surface,
+					 int              src_x,
+					 int              src_y,
+					 int              dest_x,
+					 int              dest_y,
+					 int              width,
+					 int              height);
+
 GdkPixbuf *gdk_pixbuf_get_from_image    (GdkPixbuf   *dest,
                                          GdkImage    *src,
                                          GdkColormap *cmap,



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