From: Michael Orlitzky Date: Tue, 8 Aug 2023 00:08:59 +0000 (-0400) Subject: gdk_pixbuf_get_from_surface.h: new file copy/pasted from GDK X-Git-Tag: 0.0.1~9 X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=e14d077413f287d4fc34a8d72ba6b893b18dea06;p=libsvgtiny-pixbuf.git gdk_pixbuf_get_from_surface.h: new file copy/pasted from GDK This contains the implementation of the (deprecated) function gdk_pixbuf_get_from_surface(). The main reason we want it inline is to avoid the need to link with GTK/GDK itself in our pixbuf loader, on which GDK and GTK should depend. Specifically we don't want to have to worry about mismatched major versions of GTK. --- diff --git a/gdk_pixbuf_get_from_surface.h b/gdk_pixbuf_get_from_surface.h new file mode 100644 index 0000000..c3a5f96 --- /dev/null +++ b/gdk_pixbuf_get_from_surface.h @@ -0,0 +1,194 @@ +/* This file contains gdk_pixbuf_get_from_surface() and its + * dependencies, copy & pasted from gdk/deprecated/gdkpixbuf.c. + * Having it inline means that we don't have to link against a + * specific version of GTK, risking that (say) a gtk4-linked + * pixbuf loader gets used on a gtk3 desktop environment. + */ +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 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 + * + * Deprecated: 4.12: Use [class@Gdk.Texture] and subclasses instead + * cairo surfaces and pixbufs + */ +GdkPixbuf * +gdk_pixbuf_get_from_surface (cairo_surface_t *surface, + int src_x, + int src_y, + int width, + int 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); + g_clear_object (&dest); + 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; +}