/* 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;
}