/* gtkimage.c -- Load images with GDK. Copyright (C) 2020 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include "gtkimage.h" #include "coding.h" #include "blockinput.h" #include "pgtkterm.h" #include #include #ifdef USE_CAIRO #define PUT_PIXEL image_pix_container_put_pixel #define NO_PIXMAP 0 #define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b)) #endif static Lisp_Object image_spec_value (Lisp_Object spec, Lisp_Object key, bool *found) { Lisp_Object tail; eassert (valid_image_p (spec)); for (tail = XCDR (spec); CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail))) { if (EQ (XCAR (tail), key)) { if (found) *found = 1; return XCAR (XCDR (tail)); } } if (found) *found = 0; return Qnil; } static Emacs_Pix_Container image_create_pix_container (struct frame *f, unsigned int width, unsigned int height, unsigned int depth) { Emacs_Pix_Container pimg; pimg = xmalloc (sizeof (*pimg)); pimg->width = width; pimg->height = height; pimg->bits_per_pixel = depth == 1 ? 8 : 32; pimg->bytes_per_line = cairo_format_stride_for_width ((depth == 1 ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_RGB24), width); pimg->data = xmalloc (pimg->bytes_per_line * height); return pimg; } typedef void Picture; static bool image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int depth, Emacs_Pix_Container *pimg, Emacs_Pixmap *pixmap, Picture *picture) { eassert (input_blocked_p ()); /* Allocate a pixmap of the same size. */ *pixmap = image_create_pix_container (f, width, height, depth); *pimg = *pixmap; return 1; } static bool image_create_x_image_and_pixmap (struct frame *f, struct image *img, int width, int height, int depth, Emacs_Pix_Container *ximg, bool mask_p) { eassert ((!mask_p ? img->pixmap : img->mask) == NO_PIXMAP); Picture *picture = NULL; return image_create_x_image_and_pixmap_1 (f, width, height, depth, ximg, !mask_p ? &img->pixmap : &img->mask, picture); } static void gimg_set_cr_source_with_color (cairo_t *cr, struct frame *f, unsigned long color) { block_input (); Emacs_Color col; col.pixel = color; pgtk_query_color (f, &col); cairo_set_source_rgba (cr, col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0, 1.0); unblock_input (); } static void image_pix_container_put_pixel (Emacs_Pix_Container image, int x, int y, unsigned long pixel) { if (image->bits_per_pixel == 32) ((uint32_t *)(image->data + y * image->bytes_per_line))[x] = pixel; else ((uint8_t *)(image->data + y * image->bytes_per_line))[x] = pixel; } static unsigned long lookup_rgb_color (struct frame *f, int r, int g, int b) { return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8); } bool gdk_load (struct frame *f, struct image *img) { block_input (); Lisp_Object specified_file; Lisp_Object specified_data; specified_file = image_spec_value (img->spec, QCfile, NULL); specified_data = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (specified_file)) return false; specified_file = image_find_image_file (specified_file); if (!STRINGP (specified_file)) return false; GError *error = NULL; GdkTexture *text; if (STRINGP (specified_file)) text = gdk_texture_new_from_file (g_file_new_for_path (SSDATA (ENCODE_FILE (specified_file))), &error); else if (STRINGP (specified_data)) return false; // TODO! else return false; if (error) { g_error_free (error); return false; } Emacs_Pix_Context xpx = NULL; ptrdiff_t h = gdk_texture_get_height (text), w = gdk_texture_get_width (text); if (!image_create_x_image_and_pixmap (f, img, w, h, 0, &xpx, 0)) { g_object_unref (text); return false; } cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_texture_get_width (text), gdk_texture_get_height (text)); cairo_t *cr = cairo_create (surface); gimg_set_cr_source_with_color (cr, f, FRAME_BACKGROUND_COLOR (f)); cairo_rectangle (cr, 0, 0, gdk_texture_get_width (text), gdk_texture_get_height (text)); cairo_paint (cr); cairo_surface_flush (surface); cairo_surface_t *vsuf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_texture_get_width (text), gdk_texture_get_height (text)); gdk_texture_download (text, cairo_image_surface_get_data (vsuf), cairo_image_surface_get_stride (vsuf)); cairo_surface_mark_dirty (vsuf); cairo_set_source_surface (cr, vsuf, 0, 0); cairo_paint (cr); cairo_surface_destroy (vsuf); cairo_destroy (cr); uint8_t *data = cairo_image_surface_get_data (surface); for (ptrdiff_t y = 0; y < h; ++y) { ptrdiff_t idx = w * y; for (ptrdiff_t x = 0; x < w; ++x) { struct { #ifdef WORDS_BIGENDIAN uint8_t a, r, g, b; #else uint8_t b, g, r, a; #endif } cvals = *((typeof (cvals) *) data + idx + x); PUT_PIXEL (xpx, x, y, lookup_rgb_color (f, 256 * cvals.r, 256 * cvals.g, 256 * cvals.b)); } } img->height = h; img->width = w; img->mask = NULL; IMAGE_BACKGROUND (img, f, (Emacs_Pix_Context) xpx); g_object_unref (text); cairo_surface_destroy (surface); unblock_input (); return true; }