| /* GdkPixbuf library - convert X drawable information to RGB |
| * |
| * Copyright (C) 1999 Michael Zucchi |
| * |
| * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au> |
| * Cody Russell <bratsche@dfw.net> |
| * Federico Mena-Quintero <federico@gimp.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include "gdkpixbuf.h" |
| |
| #include "gdksurface.h" |
| #include "gdkinternals.h" |
| #include "gdktextureprivate.h" |
| |
| #include <gdk-pixbuf/gdk-pixbuf.h> |
| |
| /** |
| * SECTION:pixbufs |
| * @Short_description: Functions for obtaining pixbufs |
| * @Title: GdkPixbuf Interaction |
| * |
| * Pixbufs are client-side images. For details on how to create |
| * and manipulate pixbufs, see the #GdkPixbuf API documentation. |
| * |
| * The functions described here allow to obtain pixbufs from |
| * #GdkSurfaces and cairo surfaces. |
| */ |
| |
| 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 src_x, |
| int src_y, |
| int width, |
| int height) |
| { |
| cairo_surface_t *copy; |
| cairo_t *cr; |
| |
| 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, -src_x, -src_y); |
| 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 width, |
| int height) |
| { |
| int x, y; |
| |
| 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 width, |
| int height) |
| { |
| int x, y; |
| |
| 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: |
| * @surface: surface to copy from |
| * @src_x: Source X coordinate within @surface |
| * @src_y: Source Y coordinate within @surface |
| * @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. |
| * |
| * This function will create an RGB pixbuf with 8 bits per channel. |
| * The pixbuf will contain an alpha channel if the @surface contains one. |
| * |
| * Returns: (nullable) (transfer full): A newly-created pixbuf with a |
| * reference count of 1, or %NULL on error |
| */ |
| GdkPixbuf * |
| gdk_pixbuf_get_from_surface (cairo_surface_t *surface, |
| gint src_x, |
| gint src_y, |
| gint width, |
| gint height) |
| { |
| cairo_content_t content; |
| GdkPixbuf *dest; |
| |
| /* General sanity checks */ |
| g_return_val_if_fail (surface != NULL, NULL); |
| g_return_val_if_fail (width > 0 && height > 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); |
| |
| if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE && |
| cairo_image_surface_get_format (surface) == gdk_cairo_format_for_content (content)) |
| surface = cairo_surface_reference (surface); |
| else |
| { |
| surface = gdk_cairo_surface_coerce_to_image (surface, content, |
| src_x, src_y, |
| width, height); |
| src_x = 0; |
| src_y = 0; |
| } |
| 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, |
| 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, |
| width, height); |
| |
| cairo_surface_destroy (surface); |
| return dest; |
| } |
| |
| /** |
| * gdk_pixbuf_get_from_texture: |
| * @texture: a #GdkTexture |
| * |
| * Creates a new pixbuf from @texture. This should generally not be used |
| * in newly written code as later stages will almost certainly convert |
| * the pixbuf back into a texture to draw it on screen. |
| * |
| * Returns: (transfer full) (nullable): a new #GdkPixbuf or %NULL |
| * in case of an error |
| */ |
| GdkPixbuf * |
| gdk_pixbuf_get_from_texture (GdkTexture *texture) |
| { |
| GdkPixbuf *pixbuf; |
| cairo_surface_t *surface; |
| int width, height; |
| |
| g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL); |
| |
| width = gdk_texture_get_width (texture); |
| height = gdk_texture_get_height (texture); |
| surface = gdk_texture_download_surface (texture); |
| pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height); |
| cairo_surface_destroy (surface); |
| |
| return pixbuf; |
| } |