Update Android port

* doc/emacs/android.texi (Android File System): Document that
ls-lisp is now used by default.
* java/org/gnu/emacs/EmacsThread.java (EmacsThread): Name the
thread something meaningful.
* lisp/loadup.el (featurep): Load ls-lisp on Android.
* lisp/ls-lisp.el (ls-lisp-use-insert-directory-program):
Default to off on Android.
* src/android.c (android_is_directory): New fucntion.
(android_fstatat): Handle directories created by
`android_opendir'.
(android_open): Return meaningful file mode.
(struct android_dir): New fields `next', `asset_file' and `fd'.
(android_opendir): Populate those fields.
(android_dirfd): New function.
(android_closedir): Close file descriptor if set.
(android_lookup_asset_directory_fd): New function.

* src/android.h: Update prototypes.
* src/androidfont.c (androidfont_check_init): New function.
(androidfont_list, androidfont_match, androidfont_draw)
(androidfont_open_font, androidfont_close_font)
(androidfont_has_char, androidfont_encode_char)
(androidfont_text_extents, androidfont_list_family): Initialize
font driver if necessary.
(init_androidfont): Don't initialize Java font if necessary.

* src/dired.c (open_directory): Return android_dirfd if
appropriate.
(directory_files_internal, file_name_completion_dirp): Implement
correctly for Android.

* src/fileio.c (check_mutable_filename): New function.
(Fcopy_file, Fdelete_directory_internal, Fdelete_file)
(Frename_file, Fadd_name_to_file, Fmake_symbolic_link)
(Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p)
(Fverify_visited_file_modtime, Fset_visited_file_modtime): Check
that files being written to do not lie in /assets.

* src/sfntfont-android.c (GET_SCANLINE_BUFFER)
(sfntfont_android_u255to256, sfntfont_android_over_8888_1)
(sfntfont_android_over_8888, sfntfont_android_composite_bitmap):
Optimize on 64-bit ARM devices.
(sfntfont_android_put_glyphs): Optimize away memset if
background need not be filled.
This commit is contained in:
Po Lu 2023-01-21 20:03:37 +08:00
parent a03eeb0109
commit aaacf24ca2
10 changed files with 493 additions and 45 deletions

View file

@ -99,21 +99,26 @@ this varies by device.
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

View file

@ -29,6 +29,7 @@ public class EmacsThread extends Thread
public
EmacsThread (EmacsService service, boolean startDashQ)
{
super ("Emacs main thread");
this.startDashQ = startDashQ;
}

View file

@ -312,6 +312,7 @@
(if (featurep 'android)
(progn
(load "ls-lisp")
(load "term/common-win")
(load "term/android-win")))

View file

@ -184,7 +184,7 @@ if emulation is GNU then default is `(links uid gid)'."
: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

View file

@ -852,6 +852,26 @@ android_scan_directory_tree (char *file, size_t *limit_return)
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) == '/');
}
/* Intercept USER_FULL_NAME and return something that makes sense if
@ -899,8 +919,15 @@ android_fstat (int fd, struct stat *statb)
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,
@ -908,11 +935,40 @@ 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);
@ -923,7 +979,7 @@ android_fstatat (int dirfd, const char *restrict pathname,
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. */
@ -1118,7 +1174,8 @@ android_open (const char *filename, int oflag, int mode)
/* 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;
@ -4023,8 +4080,21 @@ struct android_dir
/* 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. */
@ -4034,7 +4104,7 @@ android_opendir (const char *name)
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);
@ -4052,10 +4122,19 @@ android_opendir (const char *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. */
@ -4069,6 +4148,9 @@ android_opendir (const char *name)
errno = EACCES;
}
dir->next = android_dirs;
android_dirs = dir;
return dir;
}
@ -4086,6 +4168,26 @@ android_opendir (const char *name)
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 *
@ -4152,8 +4254,29 @@ android_readdir (struct android_dir *dir)
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. */
@ -4161,6 +4284,43 @@ android_closedir (struct android_dir *dir)
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;
}
/* emacs_abort implementation for Android. This logs a stack

View file

@ -101,6 +101,7 @@ extern void android_window_updated (android_window, unsigned long);
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 *);

View file

@ -384,6 +384,41 @@ androidfont_get_cache (struct frame *frame)
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. */
@ -539,6 +574,9 @@ androidfont_list (struct frame *f, Lisp_Object font_spec)
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,
@ -595,6 +633,9 @@ androidfont_match (struct frame *f, Lisp_Object font_spec)
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,
@ -635,6 +676,9 @@ androidfont_draw (struct glyph_string *s, int from, int to,
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;
@ -683,6 +727,9 @@ androidfont_open_font (struct frame *f, Lisp_Object font_entity,
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)
@ -778,6 +825,9 @@ androidfont_close_font (struct font *font)
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. */
@ -805,6 +855,9 @@ androidfont_has_char (Lisp_Object font, int c)
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);
@ -830,6 +883,9 @@ androidfont_encode_char (struct font *font, int c)
{
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,
@ -891,6 +947,9 @@ androidfont_text_extents (struct font *font, const unsigned int *code,
jobject metrics_object;
short value;
/* Maybe initialize the font driver. */
androidfont_check_init ();
info = (struct androidfont_info *) font;
if (nglyphs == 1
@ -968,6 +1027,9 @@ androidfont_list_family (struct frame *f)
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,
@ -1042,33 +1104,14 @@ syms_of_androidfont (void)
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

View file

@ -116,6 +116,9 @@ open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
d = opendir (name);
#else
d = android_opendir (name);
if (d)
fd = android_dirfd (d);
#endif
opendir_errno = errno;
#else
@ -216,6 +219,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
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
@ -881,6 +887,13 @@ file_name_completion_dirp (int fd, struct dirent *dp, ptrdiff_t len)
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;

View file

@ -144,6 +144,25 @@ static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
struct coding_system *);
/* 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
}
/* Test whether FILE is accessible for AMODE.
Return true if successful, false (setting errno) otherwise. */
@ -2215,6 +2234,7 @@ permissions. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
check_mutable_filename (encoded_newname);
#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
@ -2474,6 +2494,8 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal,
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);
check_mutable_filename (encoded_dir);
if (rmdir (dir) != 0)
report_file_error ("Removing directory", directory);
@ -2513,6 +2535,7 @@ With a prefix argument, TRASH is nil. */)
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);
@ -2670,6 +2693,8 @@ This is what happens in interactive use with M-x. */)
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)
@ -2781,6 +2806,8 @@ This is what happens in interactive use with M-x. */)
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;
@ -2834,6 +2861,8 @@ This happens for interactive use with M-x. */)
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;
@ -3565,6 +3594,8 @@ Interactively, prompt for FILENAME, and read MODE with
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,
@ -3576,7 +3607,9 @@ command from GNU Coreutils. */)
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);
@ -3648,6 +3681,7 @@ TIMESTAMP is in the format of `current-time'. */)
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)
{
@ -3680,6 +3714,7 @@ otherwise, if FILE2 does not exist, the answer is t. */)
(Lisp_Object file1, Lisp_Object file2)
{
struct stat st1, st2;
Lisp_Object encoded;
CHECK_STRING (file1);
CHECK_STRING (file2);
@ -3696,8 +3731,11 @@ otherwise, if FILE2 does not exist, the answer is t. */)
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
{
@ -5853,6 +5891,7 @@ See Info node `(elisp)Modification Time' for more details. */)
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)
@ -5913,7 +5952,7 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */)
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;
@ -5926,7 +5965,10 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */)
/* 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);

View file

@ -22,8 +22,14 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#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"
@ -63,6 +69,8 @@ static size_t max_scanline_buffer_size;
/* 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 \
{ \
@ -94,6 +102,39 @@ static size_t max_scanline_buffer_size;
} \
} 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
/* Scale each of the four packed bytes in P in the low 16 bits of P by
@ -149,6 +190,122 @@ sfntfont_android_blend (unsigned int src, unsigned int dst)
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. */
@ -163,7 +320,7 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
{
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
@ -196,9 +353,24 @@ sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
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]);
}
}
}
@ -308,11 +480,21 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
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)
{