Emacs exposes a special directory on Android systems: the name of
the directory is @file{/assets}, and it contains the @file{etc},
@file{lisp} and @file{info} directories which are normally installed
-in @file{/usr/share/emacs} directory on GNU and Unix systems.
+in @file{/usr/share/emacs} directory on GNU and Unix systems. On
+Android systems, the Lisp emulation of @command{ls} (@pxref{ls in
+Lisp}) is also enabled by default, as the @command{ls} binary which
+comes with the system varies by manufacturer and usually does not
+support all of the features required by Emacs. One copy of
+@command{ls} shipped with some Android devices is even known to lack
+support for the @code{-l} flag.
@cindex limitations of the /assets directory
-This directory exists because Android does not extract the contents of
-application packages on to the file system while unpacking them, but
-instead requires programs like Emacs to access its contents using a
-special ``asset manager'' interface. Here are the peculiarities that
-result from such an implementation:
+ This directory exists because Android does not extract the contents
+of application packages on to the file system while unpacking them,
+but instead requires programs like Emacs to access its contents using
+a special ``asset manager'' interface. Here are the peculiarities
+that result from such an implementation:
@itemize @bullet
@item
Subprocesses (such as @command{ls}) can not run from the
-@file{/assets} directory, so Dired, and functions such as
-@code{list-directory} do not work.
+@file{/assets} directory.
@item
There are no @file{.} and @file{..} directories inside the
public
EmacsThread (EmacsService service, boolean startDashQ)
{
+ super ("Emacs main thread");
this.startDashQ = startDashQ;
}
(if (featurep 'android)
(progn
+ (load "ls-lisp")
(load "term/common-win")
(load "term/android-win")))
:group 'ls-lisp)
(defcustom ls-lisp-use-insert-directory-program
- (not (memq system-type '(ms-dos windows-nt)))
+ (not (memq system-type '(ms-dos windows-nt android)))
"Non-nil causes ls-lisp to revert back to using `insert-directory-program'.
This is useful on platforms where ls-lisp is dumped into Emacs, such as
Microsoft Windows, but you would still like to use a program to list
return NULL;
}
+/* Return whether or not the directory tree entry DIR is a
+ directory.
+
+ DIR should be a value returned by
+ `android_scan_directory_tree'. */
+
+static bool
+android_is_directory (const char *dir)
+{
+ /* If the directory is the directory tree, then it is a
+ directory. */
+ if (dir == directory_tree + 5)
+ return true;
+
+ /* Otherwise, look 5 bytes behind. If it is `/', then it is a
+ directory. */
+ return (dir - 6 >= directory_tree
+ && *(dir - 6) == '/');
+}
+
\f
/* Intercept USER_FULL_NAME and return something that makes sense if
return fstat (fd, statb);
}
+static int android_lookup_asset_directory_fd (int,
+ const char *restrict *,
+ const char *restrict);
+
/* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an
- asset, find the information for the corresponding asset. */
+ asset, find the information for the corresponding asset, and if
+ dirfd is an offset into directory_tree as returned by
+ `android_dirfd', find the information within the corresponding
+ directory tree entry. */
int
android_fstatat (int dirfd, const char *restrict pathname,
{
AAsset *asset_desc;
const char *asset;
+ const char *asset_dir;
+
+ /* Look up whether or not DIRFD belongs to an open struct
+ android_dir. */
+
+ if (dirfd != AT_FDCWD)
+ dirfd
+ = android_lookup_asset_directory_fd (dirfd, &pathname,
+ pathname);
if (dirfd == AT_FDCWD
&& asset_manager
&& (asset = android_get_asset_name (pathname)))
{
+ /* Look up whether or not PATHNAME happens to be a
+ directory. */
+ asset_dir = android_scan_directory_tree ((char *) asset,
+ NULL);
+
+ if (!asset_dir)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (android_is_directory (asset_dir))
+ {
+ memset (statbuf, 0, sizeof *statbuf);
+
+ /* Fill in the stat buffer. */
+ statbuf->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
+ return 0;
+ }
+
/* AASSET_MODE_STREAMING is fastest here. */
asset_desc = AAssetManager_open (asset_manager, asset,
AASSET_MODE_STREAMING);
memset (statbuf, 0, sizeof *statbuf);
/* Fill in the stat buffer. */
- statbuf->st_mode = S_IFREG;
+ statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
statbuf->st_size = AAsset_getLength (asset_desc);
/* Close the asset. */
/* Fill in some information that will be reported to
callers of android_fstat, among others. */
- android_table[fd].statb.st_mode = S_IFREG;
+ android_table[fd].statb.st_mode
+ = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;;
/* Owned by root. */
android_table[fd].statb.st_uid = 0;
/* And the end of the files in asset_dir. */
char *asset_limit;
+
+ /* The next struct android_dir. */
+ struct android_dir *next;
+
+ /* Path to the directory relative to /. */
+ char *asset_file;
+
+ /* File descriptor used when asset_dir is set. */
+ int fd;
};
+/* List of all struct android_dir's corresponding to an asset
+ directory that are currently open. */
+static struct android_dir *android_dirs;
+
/* Like opendir. However, return an asset directory if NAME points to
an asset. */
struct android_dir *dir;
char *asset_dir;
const char *asset_name;
- size_t limit;
+ size_t limit, length;
asset_name = android_get_asset_name (name);
return NULL;
}
+ length = strlen (name);
+
dir = xmalloc (sizeof *dir);
dir->dir = NULL;
dir->asset_dir = asset_dir;
dir->asset_limit = (char *) directory_tree + limit;
+ dir->fd = -1;
+ dir->asset_file = xzalloc (length + 2);
+
+ /* Make sure dir->asset_file is terminated with /. */
+ strcpy (dir->asset_file, name);
+ if (dir->asset_file[length - 1] != '/')
+ dir->asset_file[length] = '/';
/* Make sure dir->asset_limit is within bounds. It is a limit,
and as such can be exactly one byte past directory_tree. */
errno = EACCES;
}
+ dir->next = android_dirs;
+ android_dirs = dir;
+
return dir;
}
return dir;
}
+/* Like dirfd. However, value is not a real directory file descriptor
+ if DIR is an asset directory. */
+
+int
+android_dirfd (struct android_dir *dirp)
+{
+ int fd;
+
+ if (dirp->dir)
+ return dirfd (dirp->dir);
+ else if (dirp->fd != -1)
+ return dirp->fd;
+
+ fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+
+ /* Record this file descriptor in dirp. */
+ dirp->fd = fd;
+ return fd;
+}
+
/* Like readdir, except it understands asset directories. */
struct dirent *
void
android_closedir (struct android_dir *dir)
{
+ struct android_dir **next, *tem;
+
if (dir->dir)
closedir (dir->dir);
+ else
+ {
+ if (dir->fd != -1)
+ close (dir->fd);
+
+ /* Unlink this directory from the list of all asset manager
+ directories. */
+
+ for (next = &android_dirs; (tem = *next);)
+ {
+ if (tem == dir)
+ *next = dir->next;
+ else
+ next = &(*next)->next;
+ }
+
+ /* Free the asset file name. */
+ xfree (dir->asset_file);
+ }
/* There is no need to close anything else, as the directory tree
lies in statically allocated memory. */
xfree (dir);
}
+/* Subroutine used by android_fstatat. If DIRFD belongs to an open
+ asset directory and FILE is a relative file name, then return
+ AT_FDCWD and the absolute file name of the directory prepended to
+ FILE in *PATHNAME. Else, return DIRFD. */
+
+int
+android_lookup_asset_directory_fd (int dirfd,
+ const char *restrict *pathname,
+ const char *restrict file)
+{
+ struct android_dir *dir;
+ static char *name;
+
+ if (file[0] == '/')
+ return dirfd;
+
+ for (dir = android_dirs; dir; dir = dir->next)
+ {
+ if (dir->fd == dirfd && dirfd != -1)
+ {
+ if (name)
+ xfree (name);
+
+ /* dir->asset_file is always separator terminated. */
+ name = xzalloc (strlen (dir->asset_file)
+ + strlen (file) + 1);
+ strcpy (name, dir->asset_file);
+ strcpy (name + strlen (dir->asset_file),
+ file);
+ *pathname = name;
+ return AT_FDCWD;
+ }
+ }
+
+ return dirfd;
+}
+
\f
/* emacs_abort implementation for Android. This logs a stack
struct android_dir;
extern struct android_dir *android_opendir (const char *);
+extern int android_dirfd (struct android_dir *);
extern struct dirent *android_readdir (struct android_dir *);
extern void android_closedir (struct android_dir *);
return font_cache;
}
+/* Initialize the Java side of the font driver if it has not already
+ been initialized. This is only done whenever necessary because the
+ font driver otherwise uses a lot of memory, as it has to keep every
+ typeface open. */
+
+static void
+androidfont_check_init (void)
+{
+ jmethodID method;
+ jobject old;
+
+ if (font_driver)
+ return;
+
+ /* Log a loud message. This font driver really should not be
+ used. */
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "The Android font driver is being used."
+ " Please investigate why this is so.");
+
+ method = font_driver_class.create_font_driver;
+
+ /* Initialize the font driver on the Java side. */
+ font_driver
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ font_driver_class.class,
+ method);
+ android_exception_check ();
+
+ old = font_driver;
+ font_driver
+ = (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
+ ANDROID_DELETE_LOCAL_REF (old);
+}
+
/* Return a local reference to an instance of EmacsFontDriver$FontSpec
with the same values as FONT. */
Lisp_Object value, entity;
struct androidfont_entity *info;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
spec = androidfont_from_lisp (font_spec);
array = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
Lisp_Object entity;
struct androidfont_entity *info;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
spec = androidfont_from_lisp (font_spec);
result = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
int rc;
jobject gcontext, drawable;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
verify (sizeof (unsigned int) == sizeof (jint));
info = (struct androidfont_info *) s->font;
jobject old;
jint value;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
else if (pixel_size == 0)
struct androidfont_info *info;
int i;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
info = (struct androidfont_info *) font;
/* Free the font metrics cache if it exists. */
struct androidfont_info *info;
struct androidfont_entity *entity;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
if (FONT_ENTITY_P (font))
{
entity = (struct androidfont_entity *) XFONT_ENTITY (font);
{
struct androidfont_info *info;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
info = (struct androidfont_info *) font;
return (*android_java_env)->CallIntMethod (android_java_env,
jobject metrics_object;
short value;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
info = (struct androidfont_info *) font;
if (nglyphs == 1
jsize i, length;
const char *family;
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
family_array
= (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
void
init_androidfont (void)
{
- jmethodID method;
- jobject old;
-
android_init_font_driver ();
android_init_font_spec ();
android_init_font_metrics ();
android_init_font_object ();
android_init_integer ();
- method = font_driver_class.create_font_driver;
-
- /* Initialize the font driver on the Java side. */
- font_driver
- = (*android_java_env)->CallStaticObjectMethod (android_java_env,
- font_driver_class.class,
- method);
-
- if (!font_driver)
- memory_full (0);
-
- old = font_driver;
- font_driver
- = (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
- ANDROID_DELETE_LOCAL_REF (old);
-
- if (!font_driver)
- memory_full (0);
+ /* The Java font driver is not initialized here because it uses a lot
+ of memory. */
}
void
d = opendir (name);
#else
d = android_opendir (name);
+
+ if (d)
+ fd = android_dirfd (d);
#endif
opendir_errno = errno;
#else
Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
int fd;
+
+ /* Keep in mind that FD is not always a real file descriptor on
+ Android. */
emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd);
/* Unfortunately, we can now invoke expand-file-name and
char *subdir_name = SAFE_ALLOCA (len + 2);
memcpy (subdir_name, dp->d_name, len);
strcpy (subdir_name + len, "/");
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Check if subdir_name lies in the assets directory. */
+ if (android_file_access_p (subdir_name, F_OK))
+ return true;
+#endif
+
bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0;
SAFE_FREE ();
return dirp;
static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
struct coding_system *);
+\f
+
+/* Check that ENCODED does not lie on any special directory whose
+ contents are read only. Signal a `file-error' if it does. */
+
+static void
+check_mutable_filename (Lisp_Object encoded)
+{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!strcmp (SSDATA (encoded), "/assets")
+ || !strncmp (SSDATA (encoded), "/assets/",
+ sizeof "/assets/" - 1))
+ xsignal2 (Qfile_error,
+ build_string ("File lies on read-"
+ "only directory"),
+ encoded);
+#endif
+}
+
\f
/* Test whether FILE is accessible for AMODE.
Return true if successful, false (setting errno) otherwise. */
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_newname);
#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);
+ check_mutable_filename (encoded_dir);
+
if (rmdir (dir) != 0)
report_file_error ("Removing directory", directory);
return call1 (Qmove_file_to_trash, filename);
encoded_file = ENCODE_FILE (filename);
+ check_mutable_filename (encoded_file);
if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT)
report_file_error ("Removing old name", filename);
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_file);
+ check_mutable_filename (encoded_newname);
bool plain_rename = (case_only_rename
|| (!NILP (ok_if_already_exists)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_file);
+ check_mutable_filename (encoded_newname);
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
return Qnil;
encoded_target = ENCODE_FILE (target);
encoded_linkname = ENCODE_FILE (linkname);
+ check_mutable_filename (encoded_target);
+ check_mutable_filename (encoded_linkname);
if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
return Qnil;
command from GNU Coreutils. */)
(Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
{
+ Lisp_Object encoded;
+
CHECK_FIXNUM (mode);
int nofollow = symlink_nofollow_flag (flag);
Lisp_Object absname = Fexpand_file_name (filename,
if (!NILP (handler))
return call4 (handler, Qset_file_modes, absname, mode, flag);
- char *fname = SSDATA (ENCODE_FILE (absname));
+ encoded = ENCODE_FILE (absname);
+ check_mutable_filename (encoded);
+ char *fname = SSDATA (encoded);
mode_t imode = XFIXNUM (mode) & 07777;
if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
report_file_error ("Doing chmod", absname);
return call4 (handler, Qset_file_times, absname, timestamp, flag);
Lisp_Object encoded_absname = ENCODE_FILE (absname);
+ check_mutable_filename (encoded_absname);
if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0)
{
(Lisp_Object file1, Lisp_Object file2)
{
struct stat st1, st2;
+ Lisp_Object encoded;
CHECK_STRING (file1);
CHECK_STRING (file2);
if (!NILP (handler))
return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
+ encoded = ENCODE_FILE (absname1);
+ check_mutable_filename (encoded);
+
int err1;
- if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0)
+ if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st1, 0) == 0)
err1 = 0;
else
{
return call2 (handler, Qverify_visited_file_modtime, buf);
filename = ENCODE_FILE (BVAR (b, filename));
+ check_mutable_filename (filename);
mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0
? get_stat_mtime (&st)
error ("An indirect buffer does not have a visited file");
else
{
- register Lisp_Object filename;
+ register Lisp_Object filename, encoded;
struct stat st;
Lisp_Object handler;
/* The handler can find the file name the same way we did. */
return call2 (handler, Qset_visited_file_modtime, Qnil);
- if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (filename)), &st, 0)
+ encoded = ENCODE_FILE (filename);
+ check_mutable_filename (encoded);
+
+ if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st, 0)
== 0)
{
current_buffer->modtime = get_stat_mtime (&st);
#include <dirent.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+#endif
#include <android/api-level.h>
+#include <android/log.h>
#include "androidterm.h"
#include "sfntfont.h"
/* Return a temporary buffer for storing scan lines.
Set BUFFER to the buffer upon success. */
+#ifndef __aarch64__
+
#define GET_SCANLINE_BUFFER(buffer, height, stride) \
do \
{ \
} \
} while (false);
+#else
+
+#define GET_SCANLINE_BUFFER(buffer, height, stride) \
+ do \
+ { \
+ size_t _size; \
+ void *_temp; \
+ \
+ if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \
+ memory_full (SIZE_MAX); \
+ \
+ if (_size > scanline_buffer.buffer_size) \
+ { \
+ if (posix_memalign (&_temp, 16, _size)) \
+ memory_full (_size); \
+ free (scanline_buffer.buffer_data); \
+ (buffer) \
+ = scanline_buffer.buffer_data \
+ = _temp; \
+ scanline_buffer.buffer_size = _size; \
+ } \
+ else if (_size <= scanline_buffer.buffer_size) \
+ (buffer) = scanline_buffer.buffer_data; \
+ /* This is unreachable but clang says it is. */ \
+ else \
+ emacs_abort (); \
+ \
+ max_scanline_buffer_size \
+ = max (_size, max_scanline_buffer_size); \
+ } while (false);
+
+#endif
+
\f
/* Scale each of the four packed bytes in P in the low 16 bits of P by
return both + src;
}
+#ifdef __aarch64__
+
+/* Like U255TO256, but operates on vectors. */
+
+static uint16x8_t
+sfntfont_android_u255to256 (uint8x8_t in)
+{
+ return vaddl_u8 (vshr_n_u8 (in, 7), in);
+}
+
+/* Use processor features to efficiently composite four pixels at SRC
+ to DST. */
+
+static void
+sfntfont_android_over_8888_1 (unsigned int *src, unsigned int *dst)
+{
+ uint8x8_t alpha;
+ uint16x8_t alpha_c16, v1, v3, v4;
+ uint8x8_t b, g, r, a, v2, v5;
+ uint8x8x4_t _src, _dst;
+
+ /* Pull in src and dst.
+
+ This loads bytes, not words, so little endian ABGR becomes
+ RGBA. */
+ _src = vld4_u8 ((const uint8_t *) src);
+ _dst = vld4_u8 ((const uint8_t *) dst);
+
+ /* Load constants. */
+ v4 = vdupq_n_u16 (256);
+ v5 = vdup_n_u8 (0);
+
+ /* Load src alpha. */
+ alpha = _src.val[3];
+
+ /* alpha_c16 = 256 - 255TO256 (alpha). */
+ alpha_c16 = sfntfont_android_u255to256 (alpha);
+ alpha_c16 = vsubq_u16 (v4, alpha_c16);
+
+ /* Cout = Csrc + Cdst * alpha_c. */
+ v1 = vaddl_u8 (_dst.val[2], v5);
+ v2 = _src.val[2];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ b = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+ v1 = vaddl_u8 (_dst.val[1], v5);
+ v2 = _src.val[1];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ g = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+ v1 = vaddl_u8 (_dst.val[0], v5);
+ v2 = _src.val[0];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ r = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+#if 0
+ /* Aout = Asrc + Adst * alpha_c. */
+ v1 = vaddl_u8 (_dst.val[3], v5);
+ v2 = _src.val[3];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ a = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+#else
+ /* We know that Adst is always 1, so Asrc + Adst * (1 - Asrc) is
+ always 1. */
+ a = vdup_n_u8 (255);
+#endif
+
+ /* Store back in dst. */
+ _dst.val[0] = r;
+ _dst.val[1] = g;
+ _dst.val[2] = b;
+ _dst.val[3] = a;
+ vst4_u8 ((uint8_t *) dst, _dst);
+}
+
+/* Use processor features to efficiently composite the buffer at SRC
+ to DST. Composite at most MAX - SRC words.
+
+ If either SRC or DST are not yet properly aligned, value is 1.
+ Otherwise, value is 0, and *X is incremented to the start of any
+ trailing data which could not be composited due to data alignment
+ constraints. */
+
+static int
+sfntfont_android_over_8888 (unsigned int *src, unsigned int *dst,
+ unsigned int *max, unsigned int *x)
+{
+ size_t i;
+ ptrdiff_t how_much;
+ void *s, *d;
+
+ /* Figure out how much can be composited by this loop. */
+ how_much = (max - src) & ~7;
+
+ /* Return if there is not enough to vectorize. */
+ if (!how_much)
+ return 1;
+
+ /* Now increment *X by that much so the containing loop can process
+ the remaining pixels one-by-one. */
+
+ *x += how_much;
+
+ for (i = 0; i < how_much; i += 8)
+ {
+ s = (src + i);
+ d = (dst + i);
+
+ sfntfont_android_over_8888_1 (s, d);
+ }
+
+ return 0;
+}
+
+#endif
+
/* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE
onto the native-endian ABGR8888 bitmap described by DEST and
BITMAP_INFO. RECT is the subset of the bitmap to composite. */
{
unsigned int *src_row;
unsigned int *dst_row;
- unsigned int i, src_y, x, src_x, max_x, dst_x;
+ unsigned int i, src_y, x, src_x, max_x, dst_x, lim_x;
if ((intptr_t) dest & 3 || bitmap_info->stride & 3)
/* This shouldn't be possible as Android is supposed to align the
src_x = x + (rect->x - text_rectangle->x);
dst_x = x + rect->x;
- dst_row[dst_x]
- = sfntfont_android_blend (src_row[src_x],
- dst_row[dst_x]);
+ /* This is the largest value of src_x. */
+ lim_x = max_x + (rect->x - text_rectangle->x);
+
+#ifdef __aarch64__
+ if (!sfntfont_android_over_8888 (src_row + src_x,
+ dst_row + dst_x,
+ src_row + lim_x,
+ &x))
+ {
+ /* Decrement X by one so the for loop can increment
+ it again. */
+ x--;
+ continue;
+ }
+#endif
+ dst_row[dst_x]
+ = sfntfont_android_blend (src_row[src_x],
+ dst_row[dst_x]);
}
}
}
gui_union_rectangles (&background, &text_rectangle,
&text_rectangle);
- /* Allocate enough to hold text_rectangle.height, aligned to 8
- bytes. Then fill it with the background. */
+ /* Allocate enough to hold text_rectangle.height, aligned to 8 (or
+ 16) bytes. Then fill it with the background. */
+#ifndef __aarch64__
stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7;
+#else
+ stride = ((text_rectangle.width * sizeof *buffer) + 15) & ~15;
+#endif
GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride);
- memset (buffer, 0, text_rectangle.height * stride);
+
+ /* Try to optimize out this memset if the background rectangle
+ contains the whole text rectangle. */
+
+ if (!with_background || memcmp (&background, &text_rectangle,
+ sizeof text_rectangle))
+ memset (buffer, 0, text_rectangle.height * stride);
if (with_background)
{