/* GdkPixbuf library - convert X drawable information to RGB * * Copyright (C) 1999 Michael Zucchi * * Authors: Michael Zucchi * Cody Russell * Federico Mena-Quintero * * 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 . */ #include "config.h" #include "gdkpixbuf.h" #include "gdkwindow.h" #include "gdkinternals.h" #include /** * SECTION:pixbufs * @Short_description: Functions for obtaining pixbufs * @Title: Pixbufs * * 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 * #GdkWindows and cairo surfaces. */ /** * gdk_pixbuf_get_from_window: * @window: Source window * @src_x: Source X coordinate within @window * @src_y: Source Y coordinate within @window * @width: Width in pixels of region to get * @height: Height in pixels of region to get * * Transfers image data from a #GdkWindow and converts it to an RGB(A) * representation inside a #GdkPixbuf. In other words, copies * image data from a server-side drawable to a client-side RGB(A) buffer. * This allows you to efficiently read individual pixels on the client side. * * This function will create an RGB pixbuf with 8 bits per channel with * the size specified by the @width and @height arguments scaled by the * scale factor of @window. The pixbuf will contain an alpha channel if * the @window contains one. * * If 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 the window is not mapped (typically because it’s iconified/minimized * or not on the current workspace), then %NULL will be returned. * * 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.) * * Returns: (nullable) (transfer full): A newly-created pixbuf with a * reference count of 1, or %NULL on error */ GdkPixbuf * gdk_pixbuf_get_from_window (GdkWindow *src, gint src_x, gint src_y, gint width, gint height) { cairo_surface_t *surface; cairo_surface_t *copy; cairo_t *cr; GdkPixbuf *dest; g_return_val_if_fail (GDK_IS_WINDOW (src), NULL); g_return_val_if_fail (gdk_window_is_viewable (src), NULL); surface = _gdk_window_ref_cairo_surface (src); /* We do not know what happened to this surface outside of GDK. * Especially for foreign windows, they will have been modified * by external applications. * So be on the safe side and: */ cairo_surface_mark_dirty (surface); if (cairo_surface_get_content (surface) & CAIRO_CONTENT_ALPHA) copy = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width * scale, height * scale); else copy = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width * scale, height * scale); cairo_surface_set_device_scale (copy, scale, scale); 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); dest = gdk_pixbuf_get_from_surface (copy, 0, 0, width * scale, height * scale); cairo_surface_destroy (copy); cairo_surface_destroy (surface); 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 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. For #GdkWindows, use * gdk_pixbuf_get_from_window() instead. * * 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; }