mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-14 12:41:07 +00:00
15d043c5d2
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 tell Cairo to clear all its caches. https://bugzilla.gnome.org/show_bug.cgi?id=754952
294 lines
9.3 KiB
C
294 lines
9.3 KiB
C
/* 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 "gdkwindow.h"
|
||
#include "gdkinternals.h"
|
||
|
||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
||
/**
|
||
* 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 same size specified by the @width and @height arguments. 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;
|
||
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);
|
||
|
||
dest = gdk_pixbuf_get_from_surface (surface,
|
||
src_x, src_y,
|
||
width, height);
|
||
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;
|
||
}
|