diff options
Diffstat (limited to 'src/image.c')
-rw-r--r-- | src/image.c | 1338 |
1 files changed, 1002 insertions, 336 deletions
diff --git a/src/image.c b/src/image.c index 911dfc4763d..41d72964631 100644 --- a/src/image.c +++ b/src/image.c @@ -175,6 +175,31 @@ typedef struct haiku_bitmap_record Bitmap_Record; #endif +#ifdef HAVE_ANDROID +#include "androidterm.h" + +typedef struct android_bitmap_record Bitmap_Record; + +typedef struct android_image XImage; +typedef android_pixmap Pixmap; + +#define GET_PIXEL(ximg, x, y) android_get_pixel (ximg, x, y) +#define PUT_PIXEL(ximg, x, y, pixel) android_put_pixel (ximg, x, y, pixel) +#define NO_PIXMAP 0 + +#define PIX_MASK_RETAIN 0 +#define PIX_MASK_DRAW 1 + +#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b)) +#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff) +#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff) +#define BLUE_FROM_ULONG(color) ((color) & 0xff) +#define RED16_FROM_ULONG(color) (RED_FROM_ULONG (color) * 0x101) +#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG (color) * 0x101) +#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG (color) * 0x101) + +#endif + static void image_disable_image (struct frame *, struct image *); static void image_edge_detection (struct frame *, struct image *, Lisp_Object, Lisp_Object); @@ -441,32 +466,101 @@ image_reference_bitmap (struct frame *f, ptrdiff_t id) } #ifdef HAVE_PGTK + +/* Create a Cairo pattern from the bitmap BITS, which should be WIDTH + and HEIGHT in size. BITS's fill order is LSB first, meaning that + the value of the left most pixel within a byte is its least + significant bit. */ + static cairo_pattern_t * -image_create_pattern_from_pixbuf (struct frame *f, GdkPixbuf * pixbuf) +image_bitmap_to_cr_pattern (char *bits, int width, int height) { - GdkPixbuf *pb = gdk_pixbuf_add_alpha (pixbuf, TRUE, 255, 255, 255); - cairo_surface_t *surface = - cairo_surface_create_similar_image (cairo_get_target - (f->output_data.pgtk->cr_context), - CAIRO_FORMAT_A1, - gdk_pixbuf_get_width (pb), - gdk_pixbuf_get_height (pb)); + cairo_surface_t *surface; + unsigned char *data; + int stride; + cairo_pattern_t *pattern; +#ifdef WORDS_BIGENDIAN + int x; + static const unsigned char table[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + }; +#endif /* WORDS_BIGENDIAN */ - cairo_t *cr = cairo_create (surface); - gdk_cairo_set_source_pixbuf (cr, pb, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy (cr); + surface = cairo_image_surface_create (CAIRO_FORMAT_A1, width, + height); - cairo_pattern_t *pat = cairo_pattern_create_for_surface (surface); - cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT); + if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) + memory_full (0); - cairo_surface_destroy (surface); - g_object_unref (pb); + cairo_surface_flush (surface); + data = cairo_image_surface_get_data (surface); + stride = cairo_image_surface_get_stride (surface); + +#ifdef WORDS_BIGENDIAN + /* Big endian systems require that individual bytes be inverted to + compensate for the different fill order used by Cairo. */ + while (height--) + { + memcpy (data, bits, (width + 7) / 8); + for (x = 0; x < (width + 7) / 8; ++x) + data[x] = table[data[x]]; + data += stride; + bits += (width + 7) / 8; + } +#else /* !WORDS_BIGENDIAN */ + /* Cairo uses LSB first fill order for bitmaps on little-endian + systems, so copy each row over. */ + + while (height--) + { + memcpy (data, bits, (width + 7) / 8); + data += stride; + bits += (width + 7) / 8; + } +#endif /* WORDS_BIGENDIAN */ + + cairo_surface_mark_dirty (surface); + pattern = cairo_pattern_create_for_surface (surface); + if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS) + memory_full (0); - return pat; + /* The pattern now holds a reference to the surface. */ + cairo_surface_destroy (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + return pattern; } -#endif + +#endif /* HAVE_PGTK */ /* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS. */ @@ -486,6 +580,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits, return -1; #endif /* HAVE_X_WINDOWS */ +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + android_pixmap bitmap; + + bitmap = android_create_bitmap_from_data (bits, width, height); + + if (!bitmap) + return -1; +#elif defined HAVE_ANDROID + ((void) dpyinfo); + emacs_abort (); +#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */ + #ifdef HAVE_NTGUI Lisp_Object frame UNINIT; /* The value is not used. */ Emacs_Pixmap bitmap; @@ -504,46 +610,9 @@ image_create_bitmap_from_data (struct frame *f, char *bits, #endif #ifdef HAVE_PGTK - GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - FALSE, - 8, - width, - height); - { - char *sp = bits; - int mask = 0x01; - unsigned char *buf = gdk_pixbuf_get_pixels (pixbuf); - int rowstride = gdk_pixbuf_get_rowstride (pixbuf); - for (int y = 0; y < height; y++) - { - unsigned char *dp = buf + rowstride * y; - for (int x = 0; x < width; x++) - { - if (*sp & mask) - { - *dp++ = 0xff; - *dp++ = 0xff; - *dp++ = 0xff; - } - else - { - *dp++ = 0x00; - *dp++ = 0x00; - *dp++ = 0x00; - } - if ((mask <<= 1) >= 0x100) - { - mask = 0x01; - sp++; - } - } - if (mask != 0x01) - { - mask = 0x01; - sp++; - } - } - } + cairo_pattern_t *pattern; + + pattern = image_bitmap_to_cr_pattern (bits, width, height); #endif /* HAVE_PGTK */ #ifdef HAVE_HAIKU @@ -577,10 +646,8 @@ image_create_bitmap_from_data (struct frame *f, char *bits, #endif #ifdef HAVE_PGTK - dpyinfo->bitmaps[id - 1].img = pixbuf; dpyinfo->bitmaps[id - 1].depth = 1; - dpyinfo->bitmaps[id - 1].pattern = - image_create_pattern_from_pixbuf (f, pixbuf); + dpyinfo->bitmaps[id - 1].pattern = pattern; #endif #ifdef HAVE_HAIKU @@ -598,14 +665,16 @@ image_create_bitmap_from_data (struct frame *f, char *bits, dpyinfo->bitmaps[id - 1].width = width; dpyinfo->bitmaps[id - 1].refcount = 1; -#ifdef HAVE_X_WINDOWS +#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID +#ifndef ANDROID_STUBIFY dpyinfo->bitmaps[id - 1].pixmap = bitmap; +#endif /* ANDROID_STUBIFY */ dpyinfo->bitmaps[id - 1].have_mask = false; dpyinfo->bitmaps[id - 1].depth = 1; #ifdef USE_CAIRO dpyinfo->bitmaps[id - 1].stipple = NULL; #endif /* USE_CAIRO */ -#endif /* HAVE_X_WINDOWS */ +#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */ #ifdef HAVE_NTGUI dpyinfo->bitmaps[id - 1].pixmap = bitmap; @@ -616,9 +685,20 @@ image_create_bitmap_from_data (struct frame *f, char *bits, return id; } -#if defined HAVE_HAIKU || defined HAVE_NS -static char *slurp_file (int, ptrdiff_t *); -static Lisp_Object image_find_image_fd (Lisp_Object, int *); +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#include "android.h" + +/* This abstraction allows directly loading images from assets without + copying them to a file descriptor first. */ +typedef struct android_fd_or_asset image_fd; +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ +typedef int image_fd; +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ + +#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_PGTK \ + || defined HAVE_ANDROID +static char *slurp_file (image_fd, ptrdiff_t *); +static Lisp_Object image_find_image_fd (Lisp_Object, image_fd *); static bool xbm_read_bitmap_data (struct frame *, char *, char *, int *, int *, char **, bool); #endif @@ -680,25 +760,38 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) #endif #ifdef HAVE_PGTK - GError *err = NULL; - ptrdiff_t id; - void * bitmap = gdk_pixbuf_new_from_file (SSDATA (file), &err); + ptrdiff_t id, size; + int fd, width, height, rc; + char *contents, *data; + void *bitmap; - if (!bitmap) + if (!STRINGP (image_find_image_fd (file, &fd))) + return -1; + + contents = slurp_file (fd, &size); + + if (!contents) + return -1; + + rc = xbm_read_bitmap_data (f, contents, contents + size, + &width, &height, &data, 0); + + if (!rc) { - g_error_free (err); + xfree (contents); return -1; } id = image_allocate_bitmap_record (f); - dpyinfo->bitmaps[id - 1].img = bitmap; dpyinfo->bitmaps[id - 1].refcount = 1; dpyinfo->bitmaps[id - 1].file = xlispstrdup (file); - dpyinfo->bitmaps[id - 1].height = gdk_pixbuf_get_width (bitmap); - dpyinfo->bitmaps[id - 1].width = gdk_pixbuf_get_height (bitmap); + dpyinfo->bitmaps[id - 1].height = width; + dpyinfo->bitmaps[id - 1].width = height; dpyinfo->bitmaps[id - 1].pattern - = image_create_pattern_from_pixbuf (f, bitmap); + = image_bitmap_to_cr_pattern (data, width, height); + xfree (contents); + xfree (data); return id; #endif @@ -724,7 +817,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -773,7 +866,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -834,6 +927,73 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) xfree (contents); return id; #endif + +#ifdef HAVE_ANDROID +#ifdef ANDROID_STUBIFY + ((void) dpyinfo); + + /* This function should never be called when building stubs. */ + emacs_abort (); +#else + ptrdiff_t id, size; + int width, height, rc; + image_fd fd; + char *contents, *data; + Lisp_Object found; + android_pixmap bitmap; + + /* Look for an existing bitmap with the same name. */ + for (id = 0; id < dpyinfo->bitmaps_last; ++id) + { + if (dpyinfo->bitmaps[id].refcount + && dpyinfo->bitmaps[id].file + && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file))) + { + ++dpyinfo->bitmaps[id].refcount; + return id + 1; + } + } + + /* Search bitmap-file-path for the file, if appropriate. */ + if (openp (Vx_bitmap_file_path, file, Qnil, &found, + make_fixnum (R_OK), false, false, NULL) + < 0) + return -1; + + if (!STRINGP (image_find_image_fd (file, &fd)) + && !STRINGP (image_find_image_fd (found, &fd))) + return -1; + + contents = slurp_file (fd, &size); + + if (!contents) + return -1; + + rc = xbm_read_bitmap_data (f, contents, contents + size, + &width, &height, &data, 0); + + if (!rc) + { + xfree (contents); + return -1; + } + + xfree (contents); + bitmap = android_create_bitmap_from_data (data, width, height); + xfree (data); + + id = image_allocate_bitmap_record (f); + dpyinfo->bitmaps[id - 1].pixmap = bitmap; + dpyinfo->bitmaps[id - 1].have_mask = false; + dpyinfo->bitmaps[id - 1].refcount = 1; + dpyinfo->bitmaps[id - 1].file = xlispstrdup (file); + dpyinfo->bitmaps[id - 1].depth = 1; + dpyinfo->bitmaps[id - 1].height = height; + dpyinfo->bitmaps[id - 1].width = width; + + return id; +#endif +#endif } /* Free bitmap B. */ @@ -842,15 +1002,30 @@ static void free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm) { #ifdef HAVE_X_WINDOWS - XFreePixmap (dpyinfo->display, bm->pixmap); - if (bm->have_mask) - XFreePixmap (dpyinfo->display, bm->mask); + /* Free the pixmap and mask. Only do this if DPYINFO->display is + still set, which may not be the case if the connection has + already been closed in response to an IO error. */ + + if (dpyinfo->display) + { + XFreePixmap (dpyinfo->display, bm->pixmap); + if (bm->have_mask) + XFreePixmap (dpyinfo->display, bm->mask); + } + #ifdef USE_CAIRO if (bm->stipple) cairo_pattern_destroy (bm->stipple); #endif /* USE_CAIRO */ #endif /* HAVE_X_WINDOWS */ +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + android_free_pixmap (bm->pixmap); + + if (bm->have_mask) + android_free_pixmap (bm->pixmap); +#endif + #ifdef HAVE_NTGUI DeleteObject (bm->pixmap); #endif /* HAVE_NTGUI */ @@ -938,7 +1113,7 @@ static void image_unget_x_image (struct image *, bool, Emacs_Pix_Container); image_unget_x_image (img, mask_p, ximg) #endif -#ifdef HAVE_X_WINDOWS +#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID #ifndef USE_CAIRO static void image_sync_to_pixmaps (struct frame *, struct image *); @@ -952,6 +1127,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, int, int, int, XImage **, Pixmap *); static void x_destroy_x_image (XImage *); +#if defined HAVE_X_WINDOWS + /* Create a mask of a bitmap. Note is this not a perfect mask. It's nicer with some borders in this context */ @@ -1048,7 +1225,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id) x_destroy_x_image (mask_img); } -#endif /* HAVE_X_WINDOWS */ +#endif + +#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/ /*********************************************************************** Image types @@ -1082,7 +1261,7 @@ struct image_type #if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \ defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \ defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \ - defined HAVE_WEBP + defined HAVE_WEBP || defined HAVE_ANDROID # ifdef WINDOWSNT # define IMAGE_TYPE_INIT(f) f # else @@ -1158,6 +1337,18 @@ image_error (const char *format, ...) } static void +image_invalid_data_error (Lisp_Object data) +{ + image_error ("Invalid image data `%s'", data); +} + +static void +image_not_found_error (Lisp_Object filename) +{ + image_error ("Cannot find image file `%s'", filename); +} + +static void image_size_error (void) { image_error ("Invalid image size (see `max-image-size')"); @@ -1352,7 +1543,7 @@ parse_image_spec (Lisp_Object spec, struct image_keyword *keywords, if KEY is not present in SPEC. Set *FOUND depending on whether KEY was found in SPEC. */ -static Lisp_Object +Lisp_Object image_spec_value (Lisp_Object spec, Lisp_Object key, bool *found) { Lisp_Object tail; @@ -1584,7 +1775,7 @@ prepare_image_for_display (struct frame *f, struct image *img) } unblock_input (); } -#elif defined HAVE_X_WINDOWS +#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID if (!img->load_failed_p) { block_input (); @@ -1791,7 +1982,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags) /* NOTE (HAVE_NS): background color is NOT an indexed color! */ img->background_valid = 0; } -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO if (img->ximg) { image_destroy_x_image (img->ximg); @@ -1809,7 +2000,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags) img->mask = NO_PIXMAP; img->background_transparent_valid = 0; } -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO if (img->mask_img) { image_destroy_x_image (img->mask_img); @@ -2151,6 +2342,7 @@ evicted. */) { if (!NILP (animation_cache)) { + CHECK_CONS (animation_cache); #if defined (HAVE_WEBP) || defined (HAVE_GIF) anim_prune_animation_cache (XCDR (animation_cache)); #endif @@ -2181,11 +2373,11 @@ image_size_in_bytes (struct image *img) if (msk) size += msk->height * msk->bytes_per_line; -#elif defined HAVE_X_WINDOWS - /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask, - to avoid having to query the server. */ +#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID + /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp + for mask, to avoid having to query the server. */ if (img->pixmap != NO_PIXMAP) - size += img->width * img->height * 3; + size += img->width * img->height * 4; if (img->mask != NO_PIXMAP) size += img->width * img->height / 8; @@ -2520,11 +2712,11 @@ compute_image_size (double width, double height, finally move the origin back to the top left of the image, which may now be a different corner. - Note that different GUI backends (X, Cairo, w32, NS, Haiku) want - the transform matrix defined as transform from the original image - to the transformed image, while others want the matrix to describe - the transform of the space, which boils down to inverting the - matrix. + Note that different GUI backends (X, Cairo, w32, NS, Haiku, + Android) want the transform matrix defined as transform from the + original image to the transformed image, while others want the + matrix to describe the transform of the space, which boils down to + inverting the matrix. It's possible to pre-calculate the matrix multiplications and just generate one transform matrix that will do everything we need in a @@ -2566,6 +2758,96 @@ compute_image_rotation (struct image *img, double *rotation) *rotation = XFIXNUM (reduced_angle); } +#ifdef HAVE_ANDROID + +static void +matrix_identity (matrix3x3 matrix) +{ + memset (matrix, 0, sizeof (matrix3x3)); + + matrix[0][0] = 1.0; + matrix[1][1] = 1.0; + matrix[2][2] = 1.0; +} + +/* Translate the matrix TRANSFORM to X, Y, and then perform clockwise + rotation by the given angle THETA in radians and translate back. + As the transform is being performed in a coordinate system where Y + grows downwards, the given angle describes a clockwise + rotation. */ + +static void +matrix_rotate (matrix3x3 transform, double theta, double x, double y) +{ + matrix3x3 temp, copy; + + /* 1. Translate the matrix so X and Y are in the center. */ + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + temp[0][2] = x; + temp[1][2] = y; + + matrix3x3_mult (copy, temp, transform); + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* 2. Rotate the matrix counter-clockwise, assuming a coordinate + system where Y grows downwards. */ + + temp[0][0] = cos (theta); + temp[0][1] = -sin (theta); + temp[1][0] = sinf (theta); + temp[1][1] = cosf (theta); + + matrix3x3_mult (copy, temp, transform); + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* 3. Translate back. */ + + temp[0][2] = -x; + temp[1][2] = -y; + + matrix3x3_mult (copy, temp, transform); +} + +/* Scale the matrix TRANSFORM by -1, and then apply a TX of width, in + effect flipping the image horizontally. */ + +static void +matrix_mirror_horizontal (matrix3x3 transform, double width) +{ + matrix3x3 temp, copy; + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + temp[0][0] = -1.0f; + temp[0][2] = width; + + matrix3x3_mult (copy, temp, transform); +} + +static void +matrix_translate (matrix3x3 transform, float tx, float ty) +{ + matrix3x3 temp, copy; + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* Set the tx and ty. */ + temp[0][2] = tx; + temp[1][2] = ty; + + /* Multiply it with the transform. */ + matrix3x3_mult (copy, temp, transform); +} + +#endif + static void image_set_transform (struct frame *f, struct image *img) { @@ -2585,6 +2867,14 @@ image_set_transform (struct frame *f, struct image *img) memcpy (&img->transform, identity, sizeof identity); #endif +#if defined HAVE_ANDROID + matrix3x3 identity = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + }; +#endif + # if (defined HAVE_IMAGEMAGICK \ && !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE) /* ImageMagick images already have the correct transform. */ @@ -2622,7 +2912,8 @@ image_set_transform (struct frame *f, struct image *img) /* Determine flipping. */ flip = !NILP (image_spec_value (img->spec, QCflip, NULL)); -# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_ANDROID /* We want scale up operations to use a nearest neighbor filter to show real pixels instead of munging them, but scale down operations to use a blended filter, to avoid aliasing and the like. @@ -2644,7 +2935,7 @@ image_set_transform (struct frame *f, struct image *img) matrix3x3 matrix = { -# if defined USE_CAIRO || defined HAVE_XRENDER +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID [0][0] = (!IEEE_FLOATING_POINT && width == 0 ? DBL_MAX : img->width / (double) width), [1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX @@ -2667,7 +2958,7 @@ image_set_transform (struct frame *f, struct image *img) /* Haiku needs this, since the transformation is done on the basis of the view, and not the image. */ -#ifdef HAVE_HAIKU +#if defined HAVE_HAIKU int extra_tx, extra_ty; extra_tx = 0; @@ -2678,8 +2969,9 @@ image_set_transform (struct frame *f, struct image *img) rotate_flag = 0; else { -# if (defined USE_CAIRO || defined HAVE_XRENDER \ - || defined HAVE_NTGUI || defined HAVE_NS \ +#ifndef HAVE_ANDROID +# if (defined USE_CAIRO || defined HAVE_XRENDER \ + || defined HAVE_NTGUI || defined HAVE_NS \ || defined HAVE_HAIKU) int cos_r, sin_r; if (rotation == 0) @@ -2706,7 +2998,7 @@ image_set_transform (struct frame *f, struct image *img) sin_r = 1; rotate_flag = 1; -#ifdef HAVE_HAIKU +#if defined HAVE_HAIKU if (!flip) extra_ty = height; extra_tx = 0; @@ -2742,7 +3034,7 @@ image_set_transform (struct frame *f, struct image *img) if (0 < rotate_flag) { -# if defined USE_CAIRO || defined HAVE_XRENDER +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID /* 1. Translate so (0, 0) is in the center of the image. */ matrix3x3 t = { [0][0] = 1, @@ -2793,6 +3085,93 @@ image_set_transform (struct frame *f, struct image *img) img->height = height; } # endif +#else + /* Calculate the inverse transform from the destination to the + source. The matrix is currently identity with scale + applied. + + This code makes more sense to me than what lies above. But + I'm not touching what works. */ + + if (rotation != 0 && rotation != 90 + && rotation != 180 && rotation != 270) + { + rotate_flag = 0; + goto bail; + } + + rotate_flag = 1; + + switch ((int) rotation + (flip ? 1 : 0)) + { + case 0: + break; + + case 90: + /* Rotate the image 90 degrees clockwise. IOW, rotate the + destination by 90 degrees counterclockwise, which is 270 + degrees clockwise. */ + matrix_rotate (matrix, M_PI * 1.5, 0, 0); + matrix_translate (matrix, -height, 0); + break; + + case 180: + /* Apply clockwise 180 degree rotation around the + center. */ + matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0); + break; + + case 270: + /* Apply 270 degree counterclockwise rotation to the + destination, which is 90 degrees clockwise. */ + matrix_rotate (matrix, M_PI * 0.5, 0, 0); + matrix_translate (matrix, 0, -width); + break; + + case 1: + /* Flipped. Apply horizontal flip. */ + matrix_mirror_horizontal (matrix, width); + break; + + case 91: + /* Apply a flip but otherwise treat this the same as 90. */ + matrix_rotate (matrix, M_PI * 1.5, 0, 0); + matrix_translate (matrix, -height, 0); + matrix_mirror_horizontal (matrix, height); + break; + + case 181: + /* Flipped 180 degrees. Apply a flip and treat this the + same as 180. */ + matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0); + matrix_mirror_horizontal (matrix, width); + break; + + case 271: + /* Flipped 270 degrees. Apply a flip and treat this the + same as 270. */ + matrix_rotate (matrix, M_PI * 0.5, 0, 0); + matrix_translate (matrix, 0, -width); + matrix_mirror_horizontal (matrix, height); + break; + } + + /* Now set img->width and img->height. Flip them if the + rotation being applied requires so. */ + + if (rotation != 270 && rotation != 90) + { + img->width = width; + img->height = height; + } + else + { + img->height = width; + img->width = height; + } + bail: + ; +#endif } if (rotate_flag < 0) @@ -2857,6 +3236,103 @@ image_set_transform (struct frame *f, struct image *img) img->transform[0][2] = extra_tx; img->transform[1][2] = extra_ty; } +# elif defined HAVE_ANDROID + /* Create a new image of the right size, then turn it into a pixmap + and set that as img->pixmap. Destroy img->mask for now (this is + not right.) */ + + struct android_image *transformed_image, *image; + struct android_transform transform; + + /* If there is no transform, simply return. */ + if (!memcmp (&matrix, &identity, sizeof matrix)) + return; + + /* First, get the source image. */ + image = image_get_x_image (f, img, false); + + /* Make the transformed image. */ + transformed_image = android_create_image (image->depth, + ANDROID_Z_PIXMAP, + NULL, img->width, + img->height); + + /* Allocate memory for that image. */ + transformed_image->data + = xmalloc (transformed_image->bytes_per_line + * transformed_image->height); + + /* Do the transform. */ + transform.m1 = matrix[0][0]; + transform.m2 = matrix[0][1]; + transform.m3 = matrix[0][2]; + transform.m4 = matrix[1][0]; + transform.m5 = matrix[1][1]; + transform.m6 = matrix[1][2]; + + if (image->depth == 24 && smoothing) + android_project_image_bilinear (image, transformed_image, + &transform); + else + android_project_image_nearest (image, transformed_image, + &transform); + + image_unget_x_image (img, false, image); + + /* Now replace the image. */ + + if (img->ximg) + image_destroy_x_image (img->ximg); + + img->ximg = transformed_image; + +#ifndef ANDROID_STUBIFY + /* Then replace the pixmap. */ + android_free_pixmap (img->pixmap); + + /* In case android_create_pixmap signals. */ + img->pixmap = ANDROID_NONE; + img->pixmap = android_create_pixmap (img->width, img->height, + transformed_image->depth); + android_put_image (img->pixmap, transformed_image); +#else + emacs_abort (); +#endif + + /* Now, transform the mask. The mask should be depth 1, and is + always transformed using a nearest neighbor filter. */ + + if (img->mask_img || img->mask) + { + image = image_get_x_image (f, img, true); + transformed_image = android_create_image (1, ANDROID_Z_PIXMAP, + NULL, img->width, + img->height); + transformed_image->data + = xmalloc (transformed_image->bytes_per_line + * transformed_image->height); + android_project_image_nearest (image, transformed_image, + &transform); + image_unget_x_image (img, true, image); + + /* Now replace the image. */ + + if (img->mask_img) + image_destroy_x_image (img->mask_img); + + img->mask_img = transformed_image; + +#ifndef ANDROID_STUBIFY + if (img->mask) + android_free_pixmap (img->mask); + + img->mask = ANDROID_NONE; + img->mask = android_create_pixmap (img->width, img->height, 1); + android_put_image (img->mask, transformed_image); +#endif + } + + /* Done! */ #endif } @@ -3085,7 +3561,7 @@ anim_prune_animation_cache (Lisp_Object clear) { struct anim_cache *cache = *pcache; if (EQ (clear, Qt) - || (EQ (clear, Qnil) && timespec_cmp (old, cache->update_time) > 0) + || (NILP (clear) && timespec_cmp (old, cache->update_time) > 0) || EQ (clear, cache->spec)) { if (cache->handle) @@ -3164,9 +3640,12 @@ mark_image_cache (struct image_cache *c) /*********************************************************************** X / NS / W32 support code + Most of this code is shared with Android to make + it easier to maintain. ***********************************************************************/ -#ifdef HAVE_X_WINDOWS +#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID + static bool x_check_image_size (XImage *ximg, int width, int height) { @@ -3182,7 +3661,11 @@ x_check_image_size (XImage *ximg, int width, int height) int bitmap_pad, depth, bytes_per_line; if (ximg) { +#ifndef HAVE_ANDROID bitmap_pad = ximg->bitmap_pad; +#else + bitmap_pad = (ximg->depth == 1 ? 8 : 32); +#endif depth = ximg->depth; bytes_per_line = ximg->bytes_per_line; } @@ -3200,16 +3683,23 @@ static bool x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, XImage **ximg, Pixmap *pixmap) { +#ifndef HAVE_ANDROID Display *display = FRAME_X_DISPLAY (f); Drawable drawable = FRAME_X_DRAWABLE (f); +#endif eassert (input_blocked_p ()); if (depth <= 0) depth = FRAME_DISPLAY_INFO (f)->n_planes; +#ifndef HAVE_ANDROID *ximg = XCreateImage (display, FRAME_X_VISUAL (f), depth, ZPixmap, 0, NULL, width, height, depth > 16 ? 32 : depth > 8 ? 16 : 8, 0); +#else + *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width, + height); +#endif if (*ximg == NULL) { image_error ("Unable to allocate X image"); @@ -3229,7 +3719,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height); /* Allocate a pixmap of the same size. */ +#ifndef HAVE_ANDROID *pixmap = XCreatePixmap (display, drawable, width, height, depth); +#else +#ifndef ANDROID_STUBIFY + *pixmap = android_create_pixmap (width, height, depth); +#else + emacs_abort (); +#endif +#endif if (*pixmap == NO_PIXMAP) { x_destroy_x_image (*ximg); @@ -3250,7 +3748,11 @@ x_destroy_x_image (XImage *ximg) ximg->data = NULL; } +#ifndef HAVE_ANDROID XDestroyImage (ximg); +#else + android_destroy_image (ximg); +#endif } # if !defined USE_CAIRO && defined HAVE_XRENDER @@ -3317,7 +3819,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth) static bool image_check_image_size (Emacs_Pix_Container ximg, int width, int height) { -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO return x_check_image_size (ximg, width, height); #else /* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases. @@ -3349,13 +3851,13 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d if (*pixmap == NO_PIXMAP) { *pimg = NULL; - image_error ("Unable to create X pixmap", Qnil, Qnil); - return 0; + image_error ("Unable to create X pixmap"); + return false; } *pimg = *pixmap; return 1; -#elif defined HAVE_X_WINDOWS +#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap)) return 0; # ifdef HAVE_XRENDER @@ -3382,8 +3884,8 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d if (*pixmap == NO_PIXMAP) { *pimg = NULL; - image_error ("Unable to create pixmap", Qnil, Qnil); - return 0; + image_error ("Unable to create pixmap"); + return false; } *pimg = *pixmap; @@ -3501,7 +4003,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d static void image_destroy_x_image (Emacs_Pix_Container pimg) { -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO x_destroy_x_image (pimg); #else eassert (input_blocked_p ()); @@ -3540,7 +4042,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg, XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0, pimg->width, pimg->height); XFreeGC (FRAME_X_DISPLAY (f), gc); -#endif /* HAVE_X_WINDOWS */ +#elif defined HAVE_ANDROID + android_put_image (pixmap, pimg); +#endif #ifdef HAVE_NS eassert (pimg == pixmap); @@ -3577,7 +4081,7 @@ static void image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg, bool mask_p) { -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO if (!mask_p) { eassert (img->ximg == NULL); @@ -3595,7 +4099,7 @@ image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg, #endif } -#if defined HAVE_X_WINDOWS && !defined USE_CAIRO +#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO /* Put the X images recorded in IMG on frame F into pixmaps, then free the X images and their buffers. */ @@ -3651,7 +4155,7 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p) { #if defined USE_CAIRO || defined (HAVE_HAIKU) return !mask_p ? img->pixmap : img->mask; -#elif defined HAVE_X_WINDOWS +#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img; if (ximg_in_img) @@ -3661,9 +4165,15 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p) return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask, 0, 0, img->original_width, img->original_height, ~0, ZPixmap); #endif +#ifndef HAVE_ANDROID else return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask, 0, 0, img->width, img->height, ~0, ZPixmap); +#else + else + return android_get_image (!mask_p ? img->pixmap : img->mask, + ANDROID_Z_PIXMAP); +#endif #elif defined (HAVE_NS) Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask; @@ -3676,13 +4186,18 @@ static void image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg) { #ifdef USE_CAIRO -#elif defined HAVE_X_WINDOWS +#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img; if (ximg_in_img) eassert (ximg == ximg_in_img); +#ifdef HAVE_ANDROID + else + android_destroy_image (ximg); +#else else XDestroyImage (ximg); +#endif #elif defined (HAVE_NS) ns_release_object (ximg); #endif @@ -3701,10 +4216,11 @@ image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg) PFD is null, do not open the file. */ static Lisp_Object -image_find_image_fd (Lisp_Object file, int *pfd) +image_find_image_fd (Lisp_Object file, image_fd *pfd) { Lisp_Object file_found, search_path; int fd; + void *platform; /* TODO I think this should use something like image-load-path instead. Unfortunately, that can contain non-string elements. */ @@ -3713,8 +4229,10 @@ image_find_image_fd (Lisp_Object file, int *pfd) Vx_bitmap_file_path); /* Try to find FILE in data-directory/images, then x-bitmap-file-path. */ + platform = NULL; fd = openp (search_path, file, Qnil, &file_found, - pfd ? Qt : make_fixnum (R_OK), false, false); + pfd ? Qt : make_fixnum (R_OK), false, false, + pfd ? &platform : NULL); if (fd == -2) { /* The file exists locally, but has a file name handler. @@ -3724,10 +4242,23 @@ image_find_image_fd (Lisp_Object file, int *pfd) Lisp_Object encoded_name = ENCODE_FILE (file_found); fd = emacs_open (SSDATA (encoded_name), O_RDONLY, 0); } - else if (fd < 0) + /* FD is -3 if PLATFORM is set to a valid asset file descriptor on + Android. */ + else if (fd < 0 && fd != -3) return Qnil; + +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY if (pfd) *pfd = fd; +#else + /* Construct an asset file descriptor. */ + + if (pfd) + { + pfd->fd = fd; + pfd->asset = platform; + } +#endif return file_found; } @@ -3741,15 +4272,26 @@ image_find_image_file (Lisp_Object file) return image_find_image_fd (file, 0); } +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + +static void +close_android_fd (void *ptr) +{ + android_close_asset (*(struct android_fd_or_asset *) ptr); +} + +#endif + /* Read FILE into memory. Value is a pointer to a buffer allocated with xmalloc holding FILE's contents. Value is null if an error occurred. FD is a file descriptor open for reading FILE. Set *SIZE to the size of the file. */ static char * -slurp_file (int fd, ptrdiff_t *size) +slurp_file (image_fd fd, ptrdiff_t *size) { - FILE *fp = fdopen (fd, "rb"); +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY + FILE *fp = emacs_fdopen (fd, "rb"); char *buf = NULL; struct stat st; @@ -3759,7 +4301,7 @@ slurp_file (int fd, ptrdiff_t *size) specpdl_ref count = SPECPDL_INDEX (); record_unwind_protect_ptr (fclose_unwind, fp); - if (fstat (fileno (fp), &st) == 0 + if (sys_fstat (fileno (fp), &st) == 0 && 0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX)) { /* Report an error if we read past the purported EOF. @@ -3777,10 +4319,64 @@ slurp_file (int fd, ptrdiff_t *size) unbind_to (count, Qnil); } +#else + char *buf; + struct stat st; + specpdl_ref count; + + if (!android_asset_fstat (fd, &st) + && (0 <= st.st_size + && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))) + { + count = SPECPDL_INDEX (); + record_unwind_protect_ptr (close_android_fd, &fd); + buf = xmalloc (st.st_size + 1); + + /* Read one byte past the end of the file. That allows + detecting if the file grows as it is being read. */ + + if (android_asset_read (fd, buf, + st.st_size + 1) == st.st_size) + *size = st.st_size; + else + { + xfree (buf); + buf = NULL; + } + + unbind_to (count, Qnil); + } + else + { + buf = NULL; + android_close_asset (fd); + } +#endif return buf; } +/* Like slurp_file above, but with added error handling. Value is + null if an error occurred. Set SIZE to the size of the file. + IMAGE_TYPE describes the image type (e.g. "PNG"). */ + +static char * +slurp_image (Lisp_Object filename, ptrdiff_t *size, const char *image_type) +{ + image_fd fd; + Lisp_Object file = image_find_image_fd (filename, &fd); + if (!STRINGP (file)) + { + image_not_found_error (filename); + return NULL; + } + char *result = slurp_file (fd, size); + if (result == NULL) + image_error ("Error loading %s image `%s'", + build_unibyte_string (image_type), + file); + return result; +} /*********************************************************************** @@ -4003,7 +4599,7 @@ xbm_scan (char **s, char *end, char *sval, int *ival) digit = char_hexdigit (c); if (digit < 0) break; - overflow |= INT_MULTIPLY_WRAPV (value, 16, &value); + overflow |= ckd_mul (&value, value, 16); value += digit; } } @@ -4013,7 +4609,7 @@ xbm_scan (char **s, char *end, char *sval, int *ival) while (*s < end && (c = *(*s)++, '0' <= c && c <= '7')) { - overflow |= INT_MULTIPLY_WRAPV (value, 8, &value); + overflow |= ckd_mul (&value, value, 8); value += c - '0'; } } @@ -4024,8 +4620,8 @@ xbm_scan (char **s, char *end, char *sval, int *ival) while (*s < end && (c = *(*s)++, c_isdigit (c))) { - overflow |= INT_MULTIPLY_WRAPV (value, 10, &value); - overflow |= INT_ADD_WRAPV (value, c - '0', &value); + overflow |= ckd_mul (&value, value, 10); + overflow |= ckd_add (&value, value, c - '0'); } } @@ -4069,7 +4665,7 @@ xbm_scan (char **s, char *end, char *sval, int *ival) if (digit < 0) return 0; - overflow |= INT_MULTIPLY_WRAPV (value, 16, &value); + overflow |= ckd_mul (&value, value, 16); value += digit; } } @@ -4205,6 +4801,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, img->picture = x_create_xrender_picture (f, img->pixmap, 0); # endif +#elif defined HAVE_ANDROID +#ifndef ANDROID_STUBIFY + img->pixmap + = android_create_pixmap_from_bitmap_data (data, img->width, img->height, + fg, bg, + FRAME_DISPLAY_INFO (f)->n_planes); +#else + emacs_abort (); +#endif #elif defined HAVE_NTGUI img->pixmap = w32_create_pixmap_from_bitmap_data (img->width, img->height, data); @@ -4270,7 +4875,7 @@ xbm_read_bitmap_data (struct frame *f, char *contents, char *end, while (0) #define expect_ident(IDENT) \ - if (LA1 == XBM_TK_IDENT && strcmp (buffer, (IDENT)) == 0) \ + if (LA1 == XBM_TK_IDENT && strcmp (buffer, IDENT) == 0) \ match (); \ else \ goto failure @@ -4490,22 +5095,10 @@ xbm_load (struct frame *f, struct image *img) file_name = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (file_name)) { - int fd; - Lisp_Object file = image_find_image_fd (file_name, &fd); - if (!STRINGP (file)) - { - image_error ("Cannot find image file `%s'", file_name); - return 0; - } - ptrdiff_t size; - char *contents = slurp_file (fd, &size); + char *contents = slurp_image (file_name, &size, "XBM"); if (contents == NULL) - { - image_error ("Error loading XBM image `%s'", file); - return 0; - } - + return false; success_p = xbm_load_image (f, img, contents, contents + size); xfree (contents); } @@ -4635,7 +5228,8 @@ xbm_load (struct frame *f, struct image *img) XPM images ***********************************************************************/ -#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) +#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \ + || defined (HAVE_ANDROID) static bool xpm_image_p (Lisp_Object object); static bool xpm_load (struct frame *f, struct image *img); @@ -4665,7 +5259,8 @@ static bool xpm_load (struct frame *f, struct image *img); #endif /* not HAVE_NTGUI */ #endif /* HAVE_XPM */ -#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU +#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS \ + || defined HAVE_HAIKU || defined HAVE_ANDROID /* Indices of image specification fields in xpm_format, below. */ @@ -4685,7 +5280,8 @@ enum xpm_keyword_index XPM_LAST }; -#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK +#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_PGTK || defined HAVE_ANDROID /* Vector of image_keyword structures describing the format of valid XPM image specifications. */ @@ -4927,7 +5523,8 @@ init_xpm_functions (void) #endif /* WINDOWSNT */ -#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK +#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_PGTK || defined HAVE_ANDROID /* Value is true if COLOR_SYMBOLS is a valid color symbols list for XPM images. Such a list must consist of conses whose car and cdr are strings. */ @@ -4963,9 +5560,9 @@ xpm_image_p (Lisp_Object object) && (! fmt[XPM_COLOR_SYMBOLS].count || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value))); } -#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */ +#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */ -#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */ +#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */ #if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK ptrdiff_t @@ -5142,12 +5739,12 @@ xpm_load (struct frame *f, struct image *img) Lisp_Object file = image_find_image_file (specified_file); if (!STRINGP (file)) { - image_error ("Cannot find image file `%s'", specified_file); + image_not_found_error (specified_file); #ifdef ALLOC_XPM_COLORS xpm_free_color_cache (); #endif SAFE_FREE (); - return 0; + return false; } file = ENCODE_FILE (file); @@ -5174,7 +5771,7 @@ xpm_load (struct frame *f, struct image *img) Lisp_Object buffer = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (buffer)) { - image_error ("Invalid image data `%s'", buffer); + image_invalid_data_error (buffer); #ifdef ALLOC_XPM_COLORS xpm_free_color_cache (); #endif @@ -5338,10 +5935,12 @@ xpm_load (struct frame *f, struct image *img) #if (defined USE_CAIRO && defined HAVE_XPM) \ || (defined HAVE_NS && !defined HAVE_XPM) \ || (defined HAVE_HAIKU && !defined HAVE_XPM) \ - || (defined HAVE_PGTK && !defined HAVE_XPM) + || (defined HAVE_PGTK && !defined HAVE_XPM) \ + || (defined HAVE_ANDROID && !defined HAVE_XPM) -/* XPM support functions for NS and Haiku where libxpm is not available, and for - Cairo. Only XPM version 3 (without any extensions) is supported. */ +/* XPM support functions for NS, Haiku and Android where libxpm is not + available, and for Cairo. Only XPM version 3 (without any + extensions) is supported. */ static void xpm_put_color_table_v (Lisp_Object, const char *, int, Lisp_Object); @@ -5470,9 +6069,7 @@ xpm_make_color_table_h (void (**put_func) (Lisp_Object, const char *, int, { *put_func = xpm_put_color_table_h; *get_func = xpm_get_color_table_h; - return make_hash_table (hashtest_equal, DEFAULT_HASH_SIZE, - DEFAULT_REHASH_SIZE, DEFAULT_REHASH_THRESHOLD, - Qnil, false); + return make_hash_table (&hashtest_equal, DEFAULT_HASH_SIZE, Weak_None, false); } static void @@ -5482,9 +6079,10 @@ xpm_put_color_table_h (Lisp_Object color_table, Lisp_Object color) { struct Lisp_Hash_Table *table = XHASH_TABLE (color_table); - Lisp_Object chars = make_unibyte_string (chars_start, chars_len), hash_code; + Lisp_Object chars = make_unibyte_string (chars_start, chars_len); - hash_lookup (table, chars, &hash_code); + hash_hash_t hash_code; + hash_lookup_get_hash (table, chars, &hash_code); hash_put (table, chars, color, hash_code); } @@ -5495,7 +6093,7 @@ xpm_get_color_table_h (Lisp_Object color_table, { struct Lisp_Hash_Table *table = XHASH_TABLE (color_table); ptrdiff_t i = - hash_lookup (table, make_unibyte_string (chars_start, chars_len), NULL); + hash_lookup (table, make_unibyte_string (chars_start, chars_len)); return i >= 0 ? HASH_VALUE (table, i) : Qnil; } @@ -5556,7 +6154,7 @@ xpm_load_image (struct frame *f, #define expect_ident(IDENT) \ if (LA1 == XPM_TK_IDENT \ - && strlen ((IDENT)) == len && memcmp ((IDENT), beg, len) == 0) \ + && strlen (IDENT) == len && memcmp (IDENT, beg, len) == 0) \ match (); \ else \ goto failure @@ -5780,21 +6378,10 @@ xpm_load (struct frame *f, file_name = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (file_name)) { - int fd; - Lisp_Object file = image_find_image_fd (file_name, &fd); - if (!STRINGP (file)) - { - image_error ("Cannot find image file `%s'", file_name); - return 0; - } - ptrdiff_t size; - char *contents = slurp_file (fd, &size); + char *contents = slurp_image (file_name, &size, "XPM"); if (contents == NULL) - { - image_error ("Error loading XPM image `%s'", file); - return 0; - } + return false; success_p = xpm_load_image (f, img, contents, contents + size); xfree (contents); @@ -5806,8 +6393,8 @@ xpm_load (struct frame *f, data = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (data)) { - image_error ("Invalid image data `%s'", data); - return 0; + image_invalid_data_error (data); + return false; } success_p = xpm_load_image (f, img, SSDATA (data), SSDATA (data) + SBYTES (data)); @@ -6077,7 +6664,8 @@ lookup_rgb_color (struct frame *f, int r, int g, int b) { #ifdef HAVE_NTGUI return PALETTERGB (r >> 8, g >> 8, b >> 8); -#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU +#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_ANDROID return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8); #else xsignal1 (Qfile_error, @@ -6136,8 +6724,8 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p) HGDIOBJ prev; #endif /* HAVE_NTGUI */ - if (INT_MULTIPLY_WRAPV (sizeof *colors, img->width, &nbytes) - || INT_MULTIPLY_WRAPV (img->height, nbytes, &nbytes) + if (ckd_mul (&nbytes, sizeof *colors, img->width) + || ckd_mul (&nbytes, nbytes, img->height) || SIZE_MAX < nbytes) memory_full (SIZE_MAX); colors = xmalloc (nbytes); @@ -6150,7 +6738,8 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p) p = colors; for (y = 0; y < img->height; ++y) { -#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU +#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU \ + && !defined HAVE_ANDROID Emacs_Color *row = p; for (x = 0; x < img->width; ++x, ++p) p->pixel = GET_PIXEL (ximg, x, y); @@ -6158,7 +6747,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p) { FRAME_TERMINAL (f)->query_colors (f, row, img->width); } -#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */ +#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */ for (x = 0; x < img->width; ++x, ++p) { p->pixel = GET_PIXEL (ximg, x, y); @@ -6169,7 +6758,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p) p->blue = BLUE16_FROM_ULONG (p->pixel); } } -#endif /* USE_CAIRO || HAVE_NS */ +#endif /* USE_CAIRO || HAVE_NS || HAVE_ANDROID */ } image_unget_x_image_or_dc (img, 0, ximg, prev); @@ -6234,7 +6823,11 @@ image_from_emacs_colors (struct frame *f, struct image *img, Emacs_Color *colors Emacs_Pix_Container ximage; Emacs_Color *p; +#ifndef HAVE_ANDROID ximage = NULL; +#else + ximage = 0; +#endif init_color_table (); @@ -6282,8 +6875,8 @@ image_detect_edges (struct frame *f, struct image *img, #define COLOR(A, X, Y) ((A) + (Y) * img->width + (X)) - if (INT_MULTIPLY_WRAPV (sizeof *new, img->width, &nbytes) - || INT_MULTIPLY_WRAPV (img->height, nbytes, &nbytes)) + if (ckd_mul (&nbytes, sizeof *new, img->width) + || ckd_mul (&nbytes, nbytes, img->height)) memory_full (SIZE_MAX); new = xmalloc (nbytes); @@ -6396,7 +6989,9 @@ image_edge_detection (struct frame *f, struct image *img, } -#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU +#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU \ + || defined HAVE_ANDROID + static void image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap, int x, int y, unsigned int width, unsigned int height, @@ -6432,8 +7027,21 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap, XFreeGC (dpy, gc); #elif HAVE_HAIKU be_draw_cross_on_pixmap (pixmap, x, y, width, height, color); +#elif HAVE_ANDROID +#ifndef ANDROID_STUBIFY + struct android_gc *gc; + + gc = android_create_gc (0, NULL); + android_set_foreground (gc, color); + android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1); + android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y); + android_free_gc (gc); +#else + emacs_abort (); +#endif #endif } + #endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */ /* Transform image IMG on frame F so that it looks disabled. */ @@ -6477,7 +7085,7 @@ image_disable_image (struct frame *f, struct image *img) #ifndef HAVE_NTGUI #ifndef HAVE_NS /* TODO: NS support, however this not needed for toolbars */ -#if !defined USE_CAIRO && !defined HAVE_HAIKU +#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID #define CrossForeground(f) BLACK_PIX_DEFAULT (f) #define MaskForeground(f) WHITE_PIX_DEFAULT (f) #else /* USE_CAIRO || HAVE_HAIKU */ @@ -6788,21 +7396,10 @@ pbm_load (struct frame *f, struct image *img) if (STRINGP (specified_file)) { - int fd; - Lisp_Object file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) - { - image_error ("Cannot find image file `%s'", specified_file); - return 0; - } - ptrdiff_t size; - contents = slurp_file (fd, &size); + contents = slurp_image (specified_file, &size, "PBM"); if (contents == NULL) - { - image_error ("Error reading `%s'", file); - return 0; - } + return false; p = contents; end = contents + size; @@ -6813,8 +7410,8 @@ pbm_load (struct frame *f, struct image *img) data = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (data)) { - image_error ("Invalid image data `%s'", data); - return 0; + image_invalid_data_error (data); + return false; } p = SSDATA (data); end = p + SBYTES (data); @@ -7456,15 +8053,18 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) if (NILP (specified_data)) { int fd; - Lisp_Object file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) + Lisp_Object file = image_find_image_file (specified_file); + + if (!STRINGP (file) + || (fd = emacs_open (SSDATA (ENCODE_FILE (file)), + O_RDONLY, 0)) < 0) { - image_error ("Cannot find image file `%s'", specified_file); - return 0; + image_not_found_error (specified_file); + return false; } /* Open the image file. */ - fp = fdopen (fd, "rb"); + fp = emacs_fdopen (fd, "rb"); if (!fp) { image_error ("Cannot open image file `%s'", file); @@ -7475,7 +8075,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) if (fread (sig, 1, sizeof sig, fp) != sizeof sig || png_sig_cmp (sig, 0, sizeof sig)) { - fclose (fp); + emacs_fclose (fp); image_error ("Not a PNG file: `%s'", file); return 0; } @@ -7484,8 +8084,8 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) { if (!STRINGP (specified_data)) { - image_error ("Invalid image data `%s'", specified_data); - return 0; + image_invalid_data_error (specified_data); + return false; } /* Read from memory. */ @@ -7529,7 +8129,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) } if (! png_ptr) { - if (fp) fclose (fp); + if (fp) emacs_fclose (fp); return 0; } @@ -7543,7 +8143,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) xfree (c->pixels); xfree (c->rows); if (c->fp) - fclose (c->fp); + emacs_fclose (c->fp); return 0; } @@ -7611,7 +8211,6 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) simple transparency, we prefer a clipping mask. */ if (!transparent_p) { - /* png_color_16 *image_bg; */ Lisp_Object specified_bg = image_spec_value (img->spec, QCbackground, NULL); Emacs_Color color; @@ -7655,8 +8254,8 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) row_bytes = png_get_rowbytes (png_ptr, info_ptr); /* Allocate memory for the image. */ - if (INT_MULTIPLY_WRAPV (row_bytes, sizeof *pixels, &nbytes) - || INT_MULTIPLY_WRAPV (nbytes, height, &nbytes)) + if (ckd_mul (&nbytes, row_bytes, sizeof *pixels) + || ckd_mul (&nbytes, nbytes, height)) memory_full (SIZE_MAX); c->pixels = pixels = xmalloc (nbytes); c->rows = rows = xmalloc (height * sizeof *rows); @@ -7668,7 +8267,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) png_read_end (png_ptr, info_ptr); if (fp) { - fclose (fp); + emacs_fclose (fp); c->fp = NULL; } @@ -8184,14 +8783,16 @@ jpeg_load_body (struct frame *f, struct image *img, if (NILP (specified_data)) { int fd; - Lisp_Object file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) + Lisp_Object file = image_find_image_file (specified_file); + if (!STRINGP (file) + || (fd = emacs_open (SSDATA (ENCODE_FILE (file)), + O_RDONLY, 0)) < 0) { - image_error ("Cannot find image file `%s'", specified_file); - return 0; + image_not_found_error (specified_file); + return false; } - fp = fdopen (fd, "rb"); + fp = emacs_fdopen (fd, "rb"); if (fp == NULL) { image_error ("Cannot open `%s'", file); @@ -8200,8 +8801,8 @@ jpeg_load_body (struct frame *f, struct image *img, } else if (!STRINGP (specified_data)) { - image_error ("Invalid image data `%s'", specified_data); - return 0; + image_invalid_data_error (specified_data); + return false; } /* Customize libjpeg's error handling to call my_error_exit when an @@ -8231,11 +8832,12 @@ jpeg_load_body (struct frame *f, struct image *img, /* Close the input file and destroy the JPEG object. */ if (fp) - fclose (fp); + emacs_fclose (fp); jpeg_destroy_decompress (&mgr->cinfo); /* If we already have an XImage, free that. */ - image_destroy_x_image (ximg); + if (ximg) + image_destroy_x_image (ximg); /* Free pixmap and colors. */ image_clear_image (f, img); return 0; @@ -8326,7 +8928,7 @@ jpeg_load_body (struct frame *f, struct image *img, jpeg_finish_decompress (&mgr->cinfo); jpeg_destroy_decompress (&mgr->cinfo); if (fp) - fclose (fp); + emacs_fclose (fp); /* Maybe fill in the background field while we have ximg handy. */ if (NILP (image_spec_value (img->spec, QCbackground, NULL))) @@ -8641,8 +9243,8 @@ tiff_load (struct frame *f, struct image *img) Lisp_Object file = image_find_image_file (specified_file); if (!STRINGP (file)) { - image_error ("Cannot find image file `%s'", specified_file); - return 0; + image_not_found_error (specified_file); + return false; } Lisp_Object encoded_file = ENCODE_FILE (file); @@ -8662,8 +9264,8 @@ tiff_load (struct frame *f, struct image *img) { if (!STRINGP (specified_data)) { - image_error ("Invalid image data `%s'", specified_data); - return 0; + image_invalid_data_error (specified_data); + return false; } /* Memory source! */ @@ -9070,7 +9672,7 @@ gif_load (struct frame *f, struct image *img) Lisp_Object file = image_find_image_file (specified_file); if (!STRINGP (file)) { - image_error ("Cannot find image file `%s'", specified_file); + image_not_found_error (specified_file); return false; } @@ -9100,17 +9702,22 @@ gif_load (struct frame *f, struct image *img) /* Get the file size so that we can report it in `image-cache-size'. */ - struct stat st; - FILE *fp = fopen (SSDATA (encoded_file), "rb"); - if (fstat (fileno (fp), &st) == 0) - byte_size = st.st_size; - fclose (fp); + { + struct stat st; + int fd; + + fd = emacs_open (SSDATA (encoded_file), O_RDONLY, + 0); + if (!sys_fstat (fd, &st)) + byte_size = st.st_size; + emacs_close (fd); + } } else { if (!STRINGP (specified_data)) { - image_error ("Invalid image data `%s'", specified_data); + image_invalid_data_error (specified_data); return false; } @@ -9682,26 +10289,15 @@ webp_load (struct frame *f, struct image *img) if (NILP (specified_data)) { - int fd; - file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) - { - image_error ("Cannot find image file `%s'", specified_file); - return false; - } - - contents = (uint8_t *) slurp_file (fd, &size); + contents = (uint8_t *) slurp_image (specified_file, &size, "WebP"); if (contents == NULL) - { - image_error ("Error loading WebP image `%s'", file); - return false; - } + return false; } else { if (!STRINGP (specified_data)) { - image_error ("Invalid image data `%s'", specified_data); + image_invalid_data_error (specified_data); return false; } contents = SDATA (specified_data); @@ -9842,22 +10438,36 @@ webp_load (struct frame *f, struct image *img) } /* Create the x image and pixmap. */ - Emacs_Pix_Container ximg, mask_img = NULL; + Emacs_Pix_Container ximg; if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, false)) goto webp_error2; - /* Create an image and pixmap serving as mask if the WebP image - contains an alpha channel. */ - if (features.has_alpha - && !image_create_x_image_and_pixmap (f, img, width, height, 1, - &mask_img, true)) + /* Find the background to use if the WebP image contains an alpha + channel. */ + Emacs_Color bg_color; + if (features.has_alpha) { - image_destroy_x_image (ximg); - image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP); - goto webp_error2; + Lisp_Object specified_bg + = image_spec_value (img->spec, QCbackground, NULL); + + /* If the user specified a color, try to use it; if not, use the + current frame background, ignoring any default background + color set by the image. */ + if (STRINGP (specified_bg)) + FRAME_TERMINAL (f)->defined_color_hook (f, + SSDATA (specified_bg), + &bg_color, + false, + false); + else + FRAME_TERMINAL (f)->query_frame_background_color (f, &bg_color); + bg_color.red >>= 8; + bg_color.green >>= 8; + bg_color.blue >>= 8; } - /* Fill the X image and mask from WebP data. */ + /* Fill the X image from WebP data. */ + init_color_table (); img->corners[TOP_CORNER] = 0; @@ -9872,21 +10482,24 @@ webp_load (struct frame *f, struct image *img) { for (int x = 0; x < width; ++x) { - int r = *p++ << 8; - int g = *p++ << 8; - int b = *p++ << 8; - PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b)); - - /* An alpha channel associates variable transparency with an - image. WebP allows up to 256 levels of partial transparency. - We handle this like with PNG (which see), using the frame's - background color to combine the image with. */ + int r, g, b; + /* The WebP alpha channel allows 256 levels of partial + transparency. Blend it with the background manually. */ if (features.has_alpha || anim) { - if (mask_img) - PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN); - ++p; + float a = (float) p[3] / UINT8_MAX; + r = (int)(a * p[0] + (1 - a) * bg_color.red) << 8; + g = (int)(a * p[1] + (1 - a) * bg_color.green) << 8; + b = (int)(a * p[2] + (1 - a) * bg_color.blue) << 8; + p += 4; + } + else + { + r = *p++ << 8; + g = *p++ << 8; + b = *p++ << 8; } + PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b)); } } @@ -9899,16 +10512,6 @@ webp_load (struct frame *f, struct image *img) /* Put ximg into the image. */ image_put_x_image (f, img, ximg, 0); - /* Same for the mask. */ - if (mask_img) - { - /* Fill in the background_transparent field while we have the - mask handy. Casting avoids a GCC warning. */ - image_background_transparent (img, f, (Emacs_Pix_Context)mask_img); - - image_put_x_image (f, img, mask_img, 1); - } - img->width = width; img->height = height; @@ -10365,7 +10968,8 @@ imagemagick_load_image (struct frame *f, struct image *img, return 0; } -#ifdef HAVE_MAGICKAUTOORIENTIMAGE +#if defined HAVE_MAGICKAUTOORIENTIMAGE \ + || HAVE_DECL_MAGICKAUTOORIENTIMAGE /* If no :rotation is explicitly specified, apply the automatic rotation from EXIF. */ if (NILP (image_spec_value (img->spec, QCrotation, NULL))) @@ -10522,7 +11126,8 @@ imagemagick_load_image (struct frame *f, struct image *img, { MagickWand *new_wand; MagickSetImageBackgroundColor (image_wand, bg_wand); -#ifdef HAVE_MAGICKMERGEIMAGELAYERS +#if defined HAVE_MAGICKMERGEIMAGELAYERS \ + || HAVE_DECL_MAGICKMERGEIMAGELAYERS new_wand = MagickMergeImageLayers (image_wand, MergeLayer); #else new_wand = MagickFlattenImages (image_wand); @@ -10551,8 +11156,9 @@ imagemagick_load_image (struct frame *f, struct image *img, init_color_table (); -#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \ - ! defined (HAVE_NS) && ! defined (HAVE_HAIKU) +#if (defined (HAVE_MAGICKEXPORTIMAGEPIXELS) \ + || HAVE_DECL_MAGICKEXPORTIMAGEPIXELS) \ + && ! defined (HAVE_NS) && ! defined (HAVE_HAIKU) if (imagemagick_render_type != 0) { /* Magicexportimage is normally faster than pixelpushing. This @@ -10700,8 +11306,8 @@ imagemagick_load (struct frame *f, struct image *img) Lisp_Object file = image_find_image_file (file_name); if (!STRINGP (file)) { - image_error ("Cannot find image file `%s'", file_name); - return 0; + image_not_found_error (file_name); + return false; } file = ENCODE_FILE (file); #ifdef WINDOWSNT @@ -10718,8 +11324,8 @@ imagemagick_load (struct frame *f, struct image *img) data = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (data)) { - image_error ("Invalid image data `%s'", data); - return 0; + image_invalid_data_error (data); + return false; } success_p = imagemagick_load_image (f, img, SDATA (data), SBYTES (data), NULL); @@ -11078,12 +11684,12 @@ svg_load (struct frame *f, struct image *img) base_uri = image_spec_value (img->spec, QCbase_uri, NULL); if (STRINGP (file_name)) { - int fd; + image_fd fd; Lisp_Object file = image_find_image_fd (file_name, &fd); if (!STRINGP (file)) { - image_error ("Cannot find image file `%s'", file_name); - return 0; + image_not_found_error (file_name); + return false; } /* Read the entire file into memory. */ @@ -11092,7 +11698,7 @@ svg_load (struct frame *f, struct image *img) if (contents == NULL) { image_error ("Error loading SVG image `%s'", file); - return 0; + return false; } /* If the file was slurped into memory properly, parse it. */ if (!STRINGP (base_uri)) @@ -11110,8 +11716,8 @@ svg_load (struct frame *f, struct image *img) data = image_spec_value (img->spec, QCdata, NULL); if (!STRINGP (data)) { - image_error ("Invalid image data `%s'", data); - return 0; + image_invalid_data_error (data); + return false; } if (!STRINGP (base_uri)) base_uri = BVAR (current_buffer, filename); @@ -11129,6 +11735,12 @@ svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size) { double value = length.length; +#if ! LIBRSVG_CHECK_VERSION (2, 48, 0) + /* librsvg 2.48 lets us define the font size, but earlier versions + default to 12 pixels. */ + font_size = 12; +#endif + switch (length.unit) { case RSVG_UNIT_PX: @@ -11153,16 +11765,31 @@ svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size) case RSVG_UNIT_IN: value *= dpi; break; -#if LIBRSVG_CHECK_VERSION (2, 48, 0) - /* We don't know exactly what font size is used on older librsvg - versions. */ case RSVG_UNIT_EM: value *= font_size; break; -#endif + case RSVG_UNIT_EX: + /* librsvg uses an ex height of half the em height, so we match + that here. */ + value = value * font_size / 2.0; + break; + case RSVG_UNIT_PERCENT: + /* Percent is a ratio of the containing "viewport". We don't + have a viewport, as such, as we try to draw the image to it's + 'natural' size rather than dictate the size as if we were + drawing icons on a toolbar or similar. This means that + percent values are useless to us and we are best off just + drawing the image according to whatever other sizes we can + derive. + + If we do set explicit width and height values in the image + spec, this will work out correctly as librsvg will still + honor the percentage sizes in its final rendering no matter + what size we make the image. */ + value = 0; + break; default: - /* Probably ex or %. We can't know what the pixel value is - without more information. */ + /* We should never reach this. */ value = 0; } @@ -11176,7 +11803,17 @@ svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size) Use librsvg to do most of the image processing. - Return true when successful. */ + Return true when successful. + + The basic process, which is used for all versions of librsvg, is to + load the SVG and parse it, then extract the image dimensions. We + then use those image dimensions to calculate the final size and + wrap the SVG data inside another SVG we build on the fly. This + wrapper does the necessary resizing and setting of foreground and + background colors and is then parsed and rasterized. + + It should also be noted that setting up the SVG prior to 2.32 was + done differently, but the overall process is the same. */ static bool svg_load_image (struct frame *f, struct image *img, char *contents, ptrdiff_t size, char *filename) @@ -11230,7 +11867,13 @@ svg_load_image (struct frame *f, struct image *img, char *contents, Lisp_Object lcss = image_spec_value (img->spec, QCcss, NULL); if (!STRINGP (lcss)) { - /* Generate the CSS for the SVG image. */ + /* Generate the CSS for the SVG image. + + We use this to set the font (font-family in CSS lingo) and + the font size. We can extend this to handle any CSS values + SVG supports, however it's only available in librsvg 2.48 and + above so some things we could set here are handled in the + wrapper below. */ /* FIXME: The below calculations leave enough space for a font size up to 9999, if it overflows we just throw an error but should probably increase the buffer size. */ @@ -11276,7 +11919,23 @@ svg_load_image (struct frame *f, struct image *img, char *contents, if (err) goto rsvg_error; #endif - /* Get the image dimensions. */ + /* Get the image dimensions. + + There are a couple of approaches used here, depending on the + contents of the SVG, and which version of librsvg we're using. + With librsvg versions prior to 2.46 we ask librsvg for the size + of the image, however this may include pats of the image that are + outside of the viewbox. + + librsvg 2.46 allows us to request the image's "intrinsic + dimensions", which are the sizes given in the SVG in CSS units. + So, for example, if the image defines it's width as "10mm", we + are given a struct that we need to translate into pixel values + ourself (see svg_css_length_to_pixels). + + 2.52 introduces a function that will give us the pixel sizes + directly, assuming we provide the correct screen DPI values. + */ #if LIBRSVG_CHECK_VERSION (2, 46, 0) gdouble gviewbox_width = 0, gviewbox_height = 0; gboolean has_viewbox = FALSE; @@ -11293,7 +11952,8 @@ svg_load_image (struct frame *f, struct image *img, char *contents, } else { - RsvgRectangle zero_rect, viewbox, out_logical_rect; + RsvgRectangle viewbox; + double explicit_width = 0, explicit_height = 0; /* Try the intrinsic dimensions first. */ gboolean has_width, has_height; @@ -11305,34 +11965,27 @@ svg_load_image (struct frame *f, struct image *img, char *contents, &has_height, &iheight, &has_viewbox, &viewbox); - if (has_width && has_height) - { - /* Success! We can use these values directly. */ - viewbox_width = svg_css_length_to_pixels (iwidth, dpi, + if (has_width) + explicit_width = svg_css_length_to_pixels (iwidth, dpi, + img->face_font_size); + if (has_height) + explicit_height = svg_css_length_to_pixels (iheight, dpi, img->face_font_size); - viewbox_height = svg_css_length_to_pixels (iheight, dpi, - img->face_font_size); - /* Here one dimension could be zero because in percent unit. - So calculate this dimension with the other. */ - if (! (0 < viewbox_width) && (iwidth.unit == RSVG_UNIT_PERCENT)) - viewbox_width = (viewbox_height * viewbox.width / viewbox.height) - * iwidth.length; - else if (! (0 < viewbox_height) && (iheight.unit == RSVG_UNIT_PERCENT)) - viewbox_height = (viewbox_width * viewbox.height / viewbox.width) - * iheight.length; + if (explicit_width > 0 && explicit_height > 0) + { + viewbox_width = explicit_width; + viewbox_height = explicit_height; } - else if (has_width && has_viewbox) + else if (explicit_width > 0 && has_viewbox) { - viewbox_width = svg_css_length_to_pixels (iwidth, dpi, - img->face_font_size); - viewbox_height = viewbox_width * viewbox.height / viewbox.width; + viewbox_width = explicit_width; + viewbox_height = explicit_width * viewbox.height / viewbox.width; } - else if (has_height && has_viewbox) + else if (explicit_height > 0 && has_viewbox) { - viewbox_height = svg_css_length_to_pixels (iheight, dpi, - img->face_font_size); - viewbox_width = viewbox_height * viewbox.width / viewbox.height; + viewbox_height = explicit_height; + viewbox_width = explicit_height * viewbox.width / viewbox.height; } else if (has_viewbox) { @@ -11346,8 +11999,15 @@ svg_load_image (struct frame *f, struct image *img, char *contents, { /* We haven't found a usable set of sizes, so try working out the visible area. */ + + /* FIXME: I'm not sure exactly how librsvg uses this + viewport input here, so I'm not sure what values I should + set. */ + RsvgRectangle max_viewport_rect = {0, 0, UINT_MAX, UINT_MAX}; + RsvgRectangle out_logical_rect; + rsvg_handle_get_geometry_for_layer (rsvg_handle, NULL, - &zero_rect, &viewbox, + &max_viewport_rect, &viewbox, &out_logical_rect, NULL); viewbox_width = viewbox.x + viewbox.width; viewbox_height = viewbox.y + viewbox.height; @@ -11467,6 +12127,8 @@ svg_load_image (struct frame *f, struct image *img, char *contents, FRAME_DISPLAY_INFO (f)->resy); #if LIBRSVG_CHECK_VERSION (2, 48, 0) + /* Set the CSS for the wrapped SVG. See the comment above the + previous use of 'css'. */ rsvg_handle_set_stylesheet (rsvg_handle, (guint8 *)css, strlen (css), NULL); #endif #else @@ -11913,7 +12575,7 @@ The list of capabilities can include one or more of the following: { #ifdef HAVE_NATIVE_TRANSFORMS # if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \ - || defined (HAVE_HAIKU) + || defined (HAVE_HAIKU) | defined HAVE_ANDROID return list2 (Qscale, Qrotate90); # elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) if (FRAME_DISPLAY_INFO (f)->xrender_supported_p) @@ -12024,7 +12686,8 @@ static struct image_type const image_types[] = { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image, IMAGE_TYPE_INIT (init_jpeg_functions) }, #endif -#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK +#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_PGTK || defined HAVE_ANDROID { SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image, IMAGE_TYPE_INIT (init_xpm_functions) }, #endif @@ -12186,7 +12849,8 @@ non-numeric, there is no explicit limit on the size of images. */); add_image_type (Qxbm); #if defined (HAVE_XPM) || defined (HAVE_NS) \ - || defined (HAVE_HAIKU) || defined (HAVE_PGTK) + || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \ + || defined (HAVE_ANDROID) DEFSYM (Qxpm, "xpm"); add_image_type (Qxpm); #endif @@ -12211,8 +12875,10 @@ non-numeric, there is no explicit limit on the size of images. */); add_image_type (Qpng); #endif -#if defined (HAVE_WEBP) || (defined (HAVE_NATIVE_IMAGE_API) \ - && defined (HAVE_HAIKU)) +#if defined (HAVE_WEBP) \ + || (defined (HAVE_NATIVE_IMAGE_API) \ + && ((defined (HAVE_NS) && defined (NS_IMPL_COCOA)) \ + || defined (HAVE_HAIKU))) DEFSYM (Qwebp, "webp"); DEFSYM (Qwebpdemux, "webpdemux"); add_image_type (Qwebp); |