Update Android port

* doc/emacs/android.texi (Android, Android Environment): Improve
documentation.
* doc/lispref/commands.texi (Touchscreen Events): Document
changes to touchscreen support.
* doc/lispref/display.texi (Defining Faces, Window Systems):
* doc/lispref/frames.texi (Frame Layout, Font and Color
Parameters):
* doc/lispref/os.texi (System Environment): Document Android in
various places.

* java/org/gnu/emacs/EmacsWindow.java (figureChange): Fix crash.
* lisp/loadup.el: ("touch-screen"): Load touch-screen.el.
* lisp/pixel-scroll.el: Autoload two functions.
* lisp/term/android-win.el: Add require 'touch-screen.
* lisp/touch-screen.el (touch-screen-current-tool)
(touch-screen-current-timer, touch-screen-delay)
(touch-screen-relative-xy, touch-screen-handle-scroll)
(touch-screen-handle-timeout, touch-screen-handle-point-update)
(touch-screen-handle-point-up, touch-screen-handle-touch)
(global-map, touch-screen): New file.
* src/android.c (android_run_debug_thread): Fix build on 64 bit
systems.
(JNICALL, android_put_pixel): Likewise.
(android_transform_coordinates, android_four_corners_bilinear)
(android_fetch_pixel_bilinear, android_project_image_bilinear)
(android_fetch_pixel_nearest_24, android_fetch_pixel_nearest_1)
(android_project_image_nearest): New functions.
* src/androidgui.h (struct android_transform): New structure.
* src/androidterm.c (android_note_mouse_movement): Remove
obsolete TODO.
(android_get_scale_factor): New function.
(android_draw_underwave): Scale underwave correctly.
* src/dispextern.h: Support native image transforms on Android.
* src/image.c (matrix_identity, matrix_rotate)
(matrix_mirror_horizontal, matrix_translate): New functions.
(image_set_transform): Implement native image transforms on
Android.
(Fimage_transforms_p): Implement on Android.

* src/keyboard.c (make_lispy_event, syms_of_keyboard): Handle
touch screen- menu bar events.
* src/sfnt.c: Fix typo in comment.
* src/sfntfont-android.c (sfntfont_android_blend, U255TO256)
(sfntfont_android_put_glyphs): Avoid redundant swizzling.
* src/sfntfont.c (sfntfont_lookup_char): Fix build on 64 bit
systems.
This commit is contained in:
Po Lu 2023-01-16 19:50:02 +08:00
parent da9ae10636
commit ad59d8986a
19 changed files with 1115 additions and 65 deletions

View file

@ -14,6 +14,7 @@ an Android device running Android 2.2 or later.
* Android Startup:: Starting up Emacs on Android.
* Android File System:: The Android file system.
* Android Environment:: Running Emacs under Android.
* Android Fonts:: Font selection under Android.
@end menu
@node What is Android?
@ -293,5 +294,56 @@ This strategy works as long as one window is in the foreground.
Otherwise, Emacs can only run in the background for a limited amount
of time before the process is killed completely.
@c TODO: write more documentation here about what is supported and
@c what is not, and fonts.
@cindex windowing limitations, android
@cindex frame parameters, android
Due to the unusual nature of the Android windowing environment, Emacs
only supports a limited subset of GUI features. Here is a list of
known limitations, and features which are not implemented:
@itemize @bullet
@item
The functions @code{raise-frame} and @code{lower-frame} are
non-functional, because of bugs in the window system.
@item
Scroll bars are not supported, as they are close to useless on Android
devices.
@item
The @code{alpha}, @code{alpha-background}, @code{z-group},
@code{override-redirect}, @code{mouse-color}, @code{cursor-color},
@code{cursor-type}, @code{title}, @code{wait-for-wm}, @code{sticky},
@code{undecorated} and @code{tool-bar-position} frame parameters
(@pxref{Frame Parameters,,, elisp, the Emacs Lisp Reference Manual})
are unsupported.
@item
The @code{fullscreen} frame parameter is always @code{maximized} for
top-level frames.
@end itemize
@node Android Fonts
@section Font backends and selection under Android
@cindex fonts, android
Emacs supports two font backends under Android: they are respectively
named @code{sfnt-android} and @code{android}.
Upon startup, Emacs enumerates all the TrueType format fonts in the
directory @file{/system/fonts}; this is where the Android system
places fonts. Emacs assumes there will always be a font named ``Droid
Sans Mono'', and then defaults to using this font. These fonts are
then rendered by the @code{sfnt-android} font driver.
When running on Android, Emacs currently lacks support for TrueType
Container and OpenType fonts. This means that only a subset of the
fonts installed on the system are currently available to Emacs. If
you are interested in raising this limitation, please contact
@email{emacs-devel@@gnu.org}.
If the @code{sfnt-android} font driver fails to find any fonts at all,
Emacs falls back to the @code{android} font driver. This is a very
lousy font driver, because of limitations and inaccuracies in the font
metrics provided by the Android platform. In that case, Emacs uses
the ``Monospace'' typeface configured on your system; this should
always be Droid Sans Mono.

View file

@ -2018,6 +2018,11 @@ display, because another program took the grab, or because the user
raised the finger from the touchscreen.
@end table
If a touchpoint is pressed against the menu bar, then Emacs will not
generate any corresponding @code{touchscreen-begin} or
@code{touchscreen-end} events; instead, the menu bar may be displayed
when @code{touchscreen-end} should have been delivered.
@node Focus Events
@subsection Focus Events
@cindex focus event

View file

@ -2846,8 +2846,9 @@ apply to. Here are the possible values of @var{characteristic}:
The kind of window system the terminal uses---either @code{graphic}
(any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable
display). @xref{Window Systems, window-system}.
Haiku), @code{pgtk} (for pure GTK), @code{android} (for Android), or
@code{tty} (a non-graphics-capable display). @xref{Window Systems,
window-system}.
@item class
What kinds of colors the terminal supports---either @code{color},
@ -8734,6 +8735,8 @@ Emacs is displaying the frame using MS-DOS direct screen writes.
Emacs is displaying the frame using the Application Kit on Haiku.
@item pgtk
Emacs is displaying the frame using pure GTK facilities.
@item android
Emacs is displaying the frame on Android.
@item nil
Emacs is displaying the frame on a character-based terminal.
@end table

View file

@ -695,7 +695,7 @@ The position of the top left corner of the native frame specifies the
indicate that position for the various builds:
@itemize @w{}
@item (1) non-toolkit, Haiku, and terminal frames
@item (1) non-toolkit, Android, Haiku, and terminal frames
@item (2) Lucid, Motif, and MS-Windows frames
@ -2389,6 +2389,7 @@ engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with
HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs
Manual}). The @code{harfbuzz} driver is similarly recommended. On
Haiku, there can be several font drivers (@pxref{Haiku Fonts,,, emacs,
The GNU Emacs Manual}), as on Android (@pxref{Android Fonts,,, emacs,
The GNU Emacs Manual}).
On other systems, there is only one available font backend, so it does

View file

@ -970,6 +970,9 @@ Hewlett-Packard HPUX operating system.
@item nacl
Google Native Client (@acronym{NaCl}) sandboxing system.
@item android
The Open Handset Alliance's Android operating system.
@item ms-dos
Microsoft's DOS@. Emacs compiled with DJGPP for MS-DOS binds
@code{system-type} to @code{ms-dos} even when you run it on MS-Windows.

View file

@ -637,8 +637,8 @@ private class Coordinate
pointerIndex = event.getActionIndex ();
pointerID = event.getPointerId (pointerIndex);
pointerMap.put (pointerID,
new Coordinate ((int) event.getX (pointerID),
(int) event.getY (pointerID)));
new Coordinate ((int) event.getX (pointerIndex),
(int) event.getY (pointerIndex)));
break;
case MotionEvent.ACTION_POINTER_UP:

View file

@ -295,6 +295,10 @@
(if (featurep 'dynamic-setting)
(load "dynamic-setting"))
;; touch-screen.el is tiny and is used liberally throughout the button
;; code etc, so it may as well be preloaded everywhere.
(load "touch-screen")
(if (featurep 'x)
(progn
(load "x-dnd")

View file

@ -500,6 +500,7 @@ Otherwise, redisplay will reset the window's vscroll."
(set-window-start nil (pixel-point-at-unseen-line) t)
(set-window-vscroll nil vscroll t))
;;;###autoload
(defun pixel-scroll-precision-scroll-down-page (delta)
"Scroll the current window down by DELTA pixels.
Note that this function doesn't work if DELTA is larger than
@ -556,6 +557,7 @@ the height of the current window."
(setq delta (- delta max-height)))
(pixel-scroll-precision-scroll-down-page delta)))
;;;###autoload
(defun pixel-scroll-precision-scroll-up-page (delta)
"Scroll the current window up by DELTA pixels.
Note that this function doesn't work if DELTA is larger than

View file

@ -37,6 +37,7 @@
(require 'mouse)
(require 'fontset)
(require 'dnd)
(require 'touch-screen)
(add-to-list 'display-format-alist '(".*" . android))

322
lisp/touch-screen.el Normal file
View file

@ -0,0 +1,322 @@
;;; touch-screen.el --- touch screen support for X and Android -*- lexical-binding: t; -*-
;; Copyright (C) 2023 Free Software Foundation, Inc.
;; Maintainer: emacs-devel@gnu.org
;; Package: emacs
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This file provides code to recognize simple touch screen gestures.
;; It is used on X and Android, where the platform cannot recognize
;; them for us.
;;; Code:
(defvar touch-screen-current-tool nil
"The touch point currently being tracked, or nil.
If non-nil, this is a list of five elements: the ID of the touch
point being tracked, the window where the touch began, a cons
containing the last known position of the touch point, relative
to that window, a field used to store data while tracking the
touch point, and the initial position of the touchpoint. See
`touch-screen-handle-point-update' for the meanings of the fourth
element.")
(defvar touch-screen-current-timer nil
"Timer used to track long-presses.
This is always cleared upon any significant state change.")
(defcustom touch-screen-delay 0.7
"Delay in seconds before Emacs considers a touch to be a long-press."
:type 'number
:group 'mouse
:version "30.1")
(defun touch-screen-relative-xy (posn window)
"Return the coordinates of POSN, a mouse position list.
However, return the coordinates relative to WINDOW.
If (posn-window posn) is the same as window, simply return the
coordinates in POSN. Otherwise, convert them to the frame, and
then back again."
(if (eq (posn-window posn) window)
(posn-x-y posn)
(let ((xy (posn-x-y posn))
(edges (window-inside-pixel-edges window)))
;; Make the X and Y positions frame relative.
(when (windowp (posn-window posn))
(let ((edges (window-inside-pixel-edges
(posn-window posn))))
(setq xy (cons (+ (car xy) (car edges))
(+ (cdr xy) (cadr edges))))))
;; Make the X and Y positions window relative again.
(cons (- (car xy) (car edges))
(- (cdr xy) (cadr edges))))))
(defun touch-screen-handle-scroll (dx dy)
"Scroll the display assuming that a touch point has moved by DX and DY."
(ignore dx)
;; This only looks good with precision pixel scrolling.
(if (> dy 0)
(pixel-scroll-precision-scroll-down-page dy)
(pixel-scroll-precision-scroll-up-page (- dy))))
(defun touch-screen-handle-timeout (arg)
"Start the touch screen timeout or handle it depending on ARG.
When ARG is nil, start the `touch-screen-current-timer' to go off
in `touch-screen-delay' seconds, and call this function with ARG
t.
When ARG is t, beep. Then, set the fourth element of
touch-screen-current-tool to `held', and the mark to the last
known position of the tool."
(if (not arg)
;; Cancel the touch screen long-press timer, if it is still
;; there by any chance.
(progn
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer))
(setq touch-screen-current-timer
(run-at-time touch-screen-delay nil
#'touch-screen-handle-timeout
t)))
;; Beep.
(beep)
;; Set touch-screen-current-timer to nil.
(setq touch-screen-current-timer nil)
(when touch-screen-current-tool
;; Set the state to `held'.
(setcar (nthcdr 3 touch-screen-current-tool) 'held)
;; Go to the initial position of the touchpoint and activate the
;; mark.
(with-selected-window (cadr touch-screen-current-tool)
(set-mark (posn-point (nth 4 touch-screen-current-tool)))
(goto-char (mark))
(activate-mark)))))
(defun touch-screen-handle-point-update (point)
"Notice that the touch point POINT has changed position.
POINT must be the touch point currently being tracked as
`touch-screen-current-tool'.
If the fourth element of `touch-screen-current-tool' is nil, then
the touch has just begun. Determine how much POINT has moved.
If POINT has moved upwards or downwards by a significant amount,
then set the fourth element to `scroll'. Then, call
`touch-screen-handle-scroll' to scroll the display by that
amount.
If the fourth element of `touch-screen-current-tool' is `scroll',
then scroll the display by how much POINT has moved in the Y
axis.
If the fourth element of `touch-screen-current-tool' is `held',
then the touch has been held down for some time. If motion
happens, cancel `touch-screen-current-timer', and set the field
to `drag'. Then, activate the mark and start dragging.
If the fourth element of `touch-screen-current-tool' is `drag',
then move point to the position of POINT.
Set `touch-screen-current-tool' to nil should any error occur."
(let ((window (nth 1 touch-screen-current-tool))
(what (nth 3 touch-screen-current-tool)))
(cond ((null what)
(let* ((posn (cdr point))
(last-posn (nth 2 touch-screen-current-tool))
;; Now get the position of X and Y relative to
;; WINDOW.
(relative-xy
(touch-screen-relative-xy posn window))
(diff-x (- (car last-posn) (car relative-xy)))
(diff-y (- (cdr last-posn) (cdr relative-xy))))
;; Decide whether or not to start scrolling.
(when (or (> diff-y 10) (> diff-x 10)
(< diff-y -10) (< diff-x -10))
(setcar (nthcdr 3 touch-screen-current-tool)
'scroll)
(setcar (nthcdr 2 touch-screen-current-tool)
relative-xy)
(with-selected-window window
(touch-screen-handle-scroll diff-x diff-y))
;; Cancel the touch screen long-press timer, if it is
;; still there by any chance.
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer)
(setq touch-screen-current-timer nil)))))
((eq what 'scroll)
;; Cancel the touch screen long-press timer, if it is still
;; there by any chance.
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer)
(setq touch-screen-current-timer nil))
(let* ((posn (cdr point))
(last-posn (nth 2 touch-screen-current-tool))
;; Now get the position of X and Y relative to
;; WINDOW.
(relative-xy
(touch-screen-relative-xy posn window))
(diff-x (- (car last-posn) (car relative-xy)))
(diff-y (- (cdr last-posn) (cdr relative-xy))))
(setcar (nthcdr 3 touch-screen-current-tool)
'scroll)
(setcar (nthcdr 2 touch-screen-current-tool)
relative-xy)
(unless (and (zerop diff-x) (zerop diff-y))
(with-selected-window window
(touch-screen-handle-scroll diff-x diff-y)))))
((eq what 'held)
(let* ((posn (cdr point))
(relative-xy
(touch-screen-relative-xy posn window)))
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer)
(setq touch-screen-current-timer nil))
;; Now start dragging.
(setcar (nthcdr 3 touch-screen-current-tool)
'drag)
(setcar (nthcdr 2 touch-screen-current-tool)
relative-xy)
(with-selected-window window
;; Activate the mark. It should have been set by the
;; time `touch-screen-timeout' was called.
(activate-mark)
;; Figure out what character to go to. If this posn is
;; in the window, go to (posn-point posn). If not,
;; then go to the line before either window start or
;; window end.
(if (and (eq (posn-window posn) window)
(posn-point posn))
(goto-char (posn-point posn))
(let ((relative-xy
(touch-screen-relative-xy posn window)))
(let ((scroll-conservatively 101))
(cond
((< (cdr relative-xy) 0)
(ignore-errors
(goto-char (1- (window-start))))
(redisplay))
((> (cdr relative-xy)
(let ((edges (window-inside-pixel-edges)))
(- (nth 3 edges) (cadr edges))))
(ignore-errors
(goto-char (1+ (window-end nil t))))
(redisplay)))))))))
((eq what 'drag)
(let* ((posn (cdr point)))
;; Keep dragging.
(with-selected-window window
;; Figure out what character to go to. If this posn is
;; in the window, go to (posn-point posn). If not,
;; then go to the line before either window start or
;; window end.
(if (and (eq (posn-window posn) window)
(posn-point posn))
(goto-char (posn-point posn))
(let ((relative-xy
(touch-screen-relative-xy posn window)))
(let ((scroll-conservatively 101))
(cond
((< (cdr relative-xy) 0)
(ignore-errors
(goto-char (1- (window-start))))
(redisplay))
((> (cdr relative-xy)
(let ((edges (window-inside-pixel-edges)))
(- (nth 3 edges) (cadr edges))))
(ignore-errors
(goto-char (1+ (window-end nil t))))
(redisplay))))))))))))
(defun touch-screen-handle-point-up (point)
"Notice that POINT has been removed from the screen.
POINT should be the point currently tracked as
`touch-screen-current-tool'.
If the fourth argument of `touch-screen-current-tool' is nil,
move point to the position of POINT, selecting the window under
POINT as well; if there is a button at POINT, then activate the
button there. Otherwise, deactivate the mark. Then, display the
on-screen keyboard."
(let ((what (nth 3 touch-screen-current-tool)))
(cond ((null what)
(when (windowp (posn-window (cdr point)))
;; Select the window that was tapped.
(select-window (posn-window (cdr point)))
(let ((button (button-at (posn-point (cdr point)))))
(when button
(button-activate button t))
(goto-char (posn-point (cdr point)))
(deactivate-mark)))))))
(defun touch-screen-handle-touch (event)
"Handle a single touch EVENT, and perform associated actions.
EVENT can either be a touchscreen-begin, touchscreen-update or
touchscreen-end event."
(interactive "e")
(cond
((eq (car event) 'touchscreen-begin)
;; A tool was just pressed against the screen. Figure out the
;; window where it is and make it the tool being tracked on the
;; window.
(let ((touchpoint (caadr event))
(position (cdadr event)))
;; Cancel the touch screen timer, if it is still there by any
;; chance.
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer)
(setq touch-screen-current-timer nil))
;; Replace any previously ongoing gesture. If POSITION has no
;; window or position, make it nil instead.
(setq touch-screen-current-tool (and (windowp (posn-window position))
(posn-point position)
(list touchpoint
(posn-window position)
(posn-x-y position)
nil position)))
;; Start the long-press timer.
(touch-screen-handle-timeout nil)))
((eq (car event) 'touchscreen-update)
;; The positions of tools currently pressed against the screen
;; have changed. If there is a tool being tracked as part of a
;; gesture, look it up in the list of tools.
(let ((new-point (assq (car touch-screen-current-tool)
(cadr event))))
(when new-point
(touch-screen-handle-point-update new-point))))
((eq (car event) 'touchscreen-end)
;; A tool has been removed from the screen. If it is the tool
;; currently being tracked, clear `touch-screen-current-tool'.
(when (eq (caadr event) (car touch-screen-current-tool))
;; Cancel the touch screen long-press timer, if it is still there
;; by any chance.
(when touch-screen-current-timer
(cancel-timer touch-screen-current-timer)
(setq touch-screen-current-timer nil))
(touch-screen-handle-point-up (cadr event))
(setq touch-screen-current-tool nil)))))
(define-key global-map [touchscreen-begin] #'touch-screen-handle-touch)
(define-key global-map [touchscreen-update] #'touch-screen-handle-touch)
(define-key global-map [touchscreen-end] #'touch-screen-handle-touch)
(provide 'touch-screen)
;;; touch-screen ends here

View file

@ -25,9 +25,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <signal.h>
#include <semaphore.h>
#include <dlfcn.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <assert.h>
@ -513,7 +515,7 @@ android_run_debug_thread (void *data)
char *line;
size_t n;
fd = (int) data;
fd = (int) (intptr_t) data;
file = fdopen (fd, "r");
if (!file)
@ -958,7 +960,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
close (pipefd[1]);
if (pthread_create (&thread, NULL, android_run_debug_thread,
(void *) pipefd[0]))
(void *) (intptr_t) pipefd[0]))
emacs_abort ();
/* Now set the path to the site load directory. */
@ -2829,6 +2831,7 @@ android_put_pixel (struct android_image *ximg, int x, int y,
{
char *byte, *word;
unsigned int r, g, b;
unsigned int pixel_int;
/* Ignore out-of-bounds accesses. */
@ -2859,7 +2862,8 @@ android_put_pixel (struct android_image *ximg, int x, int y,
b = pixel & 0x000000ff;
pixel = (r >> 16) | g | (b << 16) | 0xff000000;
memcpy (word, &pixel, sizeof pixel);
pixel_int = pixel;
memcpy (word, &pixel_int, sizeof pixel_int);
break;
}
}
@ -3734,6 +3738,262 @@ android_exception_check (void)
}
}
/* Native image transforms. */
/* Transform the coordinates X and Y by the specified affine
transformation MATRIX. Place the result in *XOUT and *YOUT. */
static void
android_transform_coordinates (int x, int y,
struct android_transform *transform,
float *xout, float *yout)
{
/* Apply the specified affine transformation.
A transform looks like:
M1 M2 M3 X
M4 M5 M6 * Y
=
M1*X + M2*Y + M3*1 = X1
M4*X + M5*Y + M6*1 = Y1
(In most transforms, there is another row at the bottom for
mathematical reasons. Since Z1 is always 1.0, the row is simply
implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See
the definition of matrix3x3 in image.c for some more explanations
about this.) */
*xout = transform->m1 * x + transform->m2 * y + transform->m3;
*yout = transform->m4 * x + transform->m5 * y + transform->m6;
}
/* Return the interpolation of the four pixels TL, TR, BL, and BR,
according to the weights DISTX and DISTY. */
static unsigned int
android_four_corners_bilinear (unsigned int tl, unsigned int tr,
unsigned int bl, unsigned int br,
int distx, int disty)
{
int distxy, distxiy, distixy, distixiy;
uint32_t f, r;
distxy = distx * disty;
distxiy = (distx << 8) - distxy;
distixy = (disty << 8) - distxy;
distixiy = (256 * 256 - (disty << 8)
- (distx << 8) + distxy);
/* Red */
r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+ (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy);
/* Green */
f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+ (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy);
r |= f & 0xff000000;
/* Now do the upper two components. */
tl >>= 16;
tr >>= 16;
bl >>= 16;
br >>= 16;
r >>= 16;
/* Blue */
f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+ (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy);
r |= f & 0x00ff0000;
/* Alpha */
f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+ (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy);
r |= f & 0xff000000;
return r;
}
/* Return the interpolation of the four pixels closest to at X, Y in
IMAGE, according to weights in both axes computed from X and Y.
IMAGE must be depth 24, or the behavior is undefined. */
static unsigned int
android_fetch_pixel_bilinear (struct android_image *image,
float x, float y)
{
int x1, y1, x2, y2;
float distx, disty;
unsigned int top_left, top_right;
unsigned int bottom_left, bottom_right;
char *word;
/* Compute the four closest corners to X and Y. */
x1 = (int) x;
x2 = x1 + 1;
y1 = (int) y;
y2 = y1 + 1;
/* Make sure all four corners are within range. */
x1 = MAX (0, MIN (image->width - 1, x1));
y1 = MAX (0, MIN (image->height - 1, y1));
x2 = MAX (0, MIN (image->width - 1, x2));
y2 = MAX (0, MIN (image->height - 1, y2));
/* Compute the X and Y biases. These are numbers between 0f and
1f. */
distx = x - x1;
disty = y - y1;
/* Fetch the four closest pixels. */
word = image->data + y1 * image->bytes_per_line + x1 * 4;
memcpy (&top_left, word, sizeof top_left);
word = image->data + y1 * image->bytes_per_line + x2 * 4;
memcpy (&top_right, word, sizeof top_right);
word = image->data + y2 * image->bytes_per_line + x1 * 4;
memcpy (&bottom_left, word, sizeof bottom_left);
word = image->data + y2 * image->bytes_per_line + x2 * 4;
memcpy (&bottom_right, word, sizeof bottom_right);
/* Do the interpolation. */
return android_four_corners_bilinear (top_left, top_right, bottom_left,
bottom_right, distx * 256,
disty * 256);
}
/* Transform the depth 24 image IMAGE by the 3x2 affine transformation
matrix MATRIX utilizing a bilinear filter. Place the result in
OUT. The matrix maps from the coordinate space of OUT to
IMAGE. */
void
android_project_image_bilinear (struct android_image *image,
struct android_image *out,
struct android_transform *transform)
{
int x, y;
unsigned int pixel;
float xout, yout;
char *word;
/* Loop through each pixel in OUT. Transform it by TRANSFORM, then
interpolate it to IMAGE, and place the result back in OUT. */
for (y = 0; y < out->height; ++y)
{
for (x = 0; x < out->width; ++x)
{
/* Transform the coordinates by TRANSFORM. */
android_transform_coordinates (x, y, transform,
&xout, &yout);
/* Interpolate back to IMAGE. */
pixel = android_fetch_pixel_bilinear (image, xout, yout);
/* Put the pixel back in OUT. */
word = out->data + y * out->bytes_per_line + x * 4;
memcpy (word, &pixel, sizeof pixel);
}
}
}
/* Return the interpolation of X, Y to IMAGE, a depth 24 image. */
static unsigned int
android_fetch_pixel_nearest_24 (struct android_image *image, float x,
float y)
{
int x1, y1;
char *word;
unsigned int pixel;
x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
word = image->data + y1 * image->bytes_per_line + x1 * 4;
memcpy (&pixel, word, sizeof pixel);
return pixel;
}
/* Return the interpolation of X, Y to IMAGE, a depth 1 image. */
static unsigned int
android_fetch_pixel_nearest_1 (struct android_image *image, float x,
float y)
{
int x1, y1;
char *byte;
x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
byte = image->data + y1 * image->bytes_per_line;
return (byte[x1 / 8] & (1 << x1 % 8)) ? 1 : 0;
}
/* Transform the depth 24 or 1 image IMAGE by the 3x2 affine
transformation matrix MATRIX. Place the result in OUT. The matrix
maps from the coordinate space of OUT to IMAGE. Use a
nearest-neighbor filter. */
void
android_project_image_nearest (struct android_image *image,
struct android_image *out,
struct android_transform *transform)
{
int x, y;
unsigned int pixel;
float xout, yout;
char *word, *byte;
if (image->depth == 1)
{
for (y = 0; y < out->height; ++y)
{
for (x = 0; x < out->width; ++x)
{
/* Transform the coordinates by TRANSFORM. */
android_transform_coordinates (x, y, transform,
&xout, &yout);
/* Interpolate back to IMAGE. */
pixel = android_fetch_pixel_nearest_1 (image, xout, yout);
/* Put the pixel back in OUT. */
byte = out->data + y * out->bytes_per_line + x / 8;
if (pixel)
*byte |= (1 << x % 8);
else
*byte &= ~(1 << x % 8);
}
}
return;
}
for (y = 0; y < out->height; ++y)
{
for (x = 0; x < out->width; ++x)
{
/* Transform the coordinates by TRANSFORM. */
android_transform_coordinates (x, y, transform,
&xout, &yout);
/* Interpolate back to IMAGE. */
pixel = android_fetch_pixel_nearest_24 (image, xout, yout);
/* Put the pixel back in OUT. */
word = out->data + y * out->bytes_per_line + x * 4;
memcpy (word, &pixel, sizeof pixel);
}
}
}
#else /* ANDROID_STUBIFY */
/* X emulation functions for Android. */
@ -3793,4 +4053,20 @@ android_put_image (android_pixmap pixmap,
emacs_abort ();
}
void
android_project_image_bilinear (struct android_image *image,
struct android_image *out,
struct android_transform *transform)
{
emacs_abort ();
}
void
android_project_image_nearest (struct android_image *image,
struct android_image *out,
struct android_transform *transform)
{
emacs_abort ();
}
#endif

View file

@ -542,6 +542,25 @@ extern struct android_image *android_get_image (android_drawable,
enum android_image_format);
extern void android_put_image (android_pixmap, struct android_image *);
/* Native image transforms. */
/* 3x2 matrix describing a projective transform. See
android_transform_coordinates for details. */
struct android_transform
{
float m1, m2, m3;
float m4, m5, m6;
};
extern void android_project_image_bilinear (struct android_image *,
struct android_image *,
struct android_transform *);
extern void android_project_image_nearest (struct android_image *,
struct android_image *,
struct android_transform *);
/* X emulation stuff also needed while building stubs. */

View file

@ -417,8 +417,6 @@ android_note_mouse_movement (struct frame *frame,
|| event->y < r->y || event->y >= r->y + r->height)
{
frame->mouse_moved = true;
/* TODO
dpyinfo->last_mouse_scroll_bar = NULL; */
note_mouse_highlight (frame, event->x, event->y);
/* Remember which glyph we're now on. */
remember_mouse_glyph (frame, event->x, event->y, r);
@ -2959,10 +2957,34 @@ android_draw_stretch_glyph_string (struct glyph_string *s)
s->background_filled_p = true;
}
static void
android_get_scale_factor (int *scale_x, int *scale_y)
{
/* This is 96 everywhere else, but 160 on Android. */
const int base_res = 160;
struct android_display_info *dpyinfo;
dpyinfo = x_display_list;
*scale_x = *scale_y = 1;
if (dpyinfo)
{
if (dpyinfo->resx > base_res)
*scale_x = floor (dpyinfo->resx / base_res);
if (dpyinfo->resy > base_res)
*scale_y = floor (dpyinfo->resy / base_res);
}
}
static void
android_draw_underwave (struct glyph_string *s, int decoration_width)
{
int wave_height = 3, wave_length = 2;
int scale_x, scale_y;
android_get_scale_factor (&scale_x, &scale_y);
int wave_height = 3 * scale_y, wave_length = 2 * scale_x;
int dx, dy, x0, y0, width, x1, y1, x2, y2, xmax;
bool odd;
struct android_rectangle wave_clip, string_clip, final_clip;

View file

@ -3098,8 +3098,9 @@ struct redisplay_interface
#ifdef HAVE_WINDOW_SYSTEM
# if (defined USE_CAIRO || defined HAVE_XRENDER \
|| defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
# if (defined USE_CAIRO || defined HAVE_XRENDER \
|| defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU \
|| defined HAVE_ANDROID)
# define HAVE_NATIVE_TRANSFORMS
# endif

View file

@ -2631,11 +2631,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
@ -2677,6 +2677,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)
{
@ -2696,6 +2786,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. */
@ -2733,7 +2831,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.
@ -2755,7 +2854,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
@ -2778,7 +2877,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;
@ -2789,8 +2888,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)
@ -2817,7 +2917,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;
@ -2853,7 +2953,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,
@ -2904,6 +3004,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)
@ -2968,6 +3155,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, false, 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
}
@ -12087,7 +12371,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)

View file

@ -339,6 +339,10 @@ static struct timespec timer_last_idleness_start_time;
static Lisp_Object virtual_core_pointer_name;
static Lisp_Object virtual_core_keyboard_name;
/* If not nil, ID of the last TOUCHSCREEN_END_EVENT to land on the
menu bar. */
static Lisp_Object menu_bar_touch_id;
/* Global variable declarations. */
@ -6445,11 +6449,74 @@ make_lispy_event (struct input_event *event)
{
Lisp_Object x, y, id, position;
struct frame *f = XFRAME (event->frame_or_window);
#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
int column, row, dummy;
#endif
id = event->arg;
x = event->x;
y = event->y;
#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
if (event->kind == TOUCHSCREEN_BEGIN_EVENT
&& coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y)))
{
/* If the tap began in the menu bar window, then save the
id. */
menu_bar_touch_id = id;
return Qnil;
}
else if (event->kind == TOUCHSCREEN_END_EVENT
&& EQ (menu_bar_touch_id, id))
{
/* This touch should activate the menu bar. Generate the
menu bar event. */
menu_bar_touch_id = Qnil;
if (f->menu_bar_window)
{
x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x),
XFIXNUM (y), &column, &row, NULL, NULL,
&dummy);
if (row >= 0 && row < FRAME_MENU_BAR_LINES (f))
{
Lisp_Object items, item;
/* Find the menu bar item under `column'. */
item = Qnil;
items = FRAME_MENU_BAR_ITEMS (f);
for (i = 0; i < ASIZE (items); i += 4)
{
Lisp_Object pos, string;
string = AREF (items, i + 1);
pos = AREF (items, i + 3);
if (NILP (string))
break;
if (column >= XFIXNUM (pos)
&& column < XFIXNUM (pos) + SCHARS (string))
{
item = AREF (items, i);
break;
}
}
/* ELisp manual 2.4b says (x y) are window
relative but code says they are
frame-relative. */
position = list4 (event->frame_or_window,
Qmenu_bar,
Fcons (event->x, event->y),
INT_TO_INTEGER (event->timestamp));
return list2 (item, position);
}
}
return Qnil;
}
#endif
position = make_lispy_position (f, x, y, event->timestamp);
return list2 (((event->kind
@ -12462,6 +12529,9 @@ syms_of_keyboard (void)
virtual_core_keyboard_name = Qnil;
staticpro (&virtual_core_keyboard_name);
menu_bar_touch_id = Qnil;
staticpro (&menu_bar_touch_id);
defsubr (&Scurrent_idle_time);
defsubr (&Sevent_symbol_parse_modifiers);
defsubr (&Sevent_convert_list);

View file

@ -3436,7 +3436,7 @@ sfnt_compare_edges (const void *a, const void *b)
that now overlap with Y, keeping them sorted by X. Poly those
edges through SPAN_FUNC. Then, move upwards by SFNT_POLY_STEP,
remove edges that no longer apply, and interpolate the remaining
edge's X coordinates. Repeat until all the edges have been polyed.
edges' X coordinates. Repeat until all the edges have been polyed.
Or alternatively, think of this as such: each edge is actually a
vector from its bottom position towards its top most position.

View file

@ -73,15 +73,16 @@ sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32)
return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff;
}
#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
/* Blend two pixels SRC and DST without utilizing any control flow.
SRC must be in premultiplied ARGB8888 format, and DST must be in
premultiplied ABGR8888 format. Value is in premultiplied ABGR8888
format. */
Both SRC and DST are expected to be in premultiplied ABGB8888
format. Value is returned in premultiplied ARGB8888 format. */
static unsigned int
sfntfont_android_blend (unsigned int src, unsigned int dst)
{
unsigned int a, br_part, ag_part, src_rb, both;
unsigned int a, br_part, ag_part, both;
a = (src >> 24);
br_part = sfntfont_android_mul8x2 (255 - a, dst);
@ -89,33 +90,6 @@ sfntfont_android_blend (unsigned int src, unsigned int dst)
both = ag_part | br_part;
/* Swizzle src. */
src_rb = src & 0x00ff00ff;
src = src & ~0x00ff00ff;
src |= (src_rb >> 16 | src_rb << 16);
/* This addition need not be saturating because both has already
been multiplied by 255 - a. */
return both + src;
}
#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
/* Blend two pixels SRC and DST without utilizing any control flow.
Both SRC and DST are expected to be in premultiplied ARGB8888
format. Value is returned in premultiplied ARGB8888 format. */
static unsigned int
sfntfont_android_blendrgb (unsigned int src, unsigned int dst)
{
unsigned int a, rb_part, ag_part, both;
a = (src >> 24);
rb_part = sfntfont_android_mul8x2 (255 - a, dst);
ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8;
both = ag_part | rb_part;
/* This addition need not be saturating because both has already
been multiplied by 255 - a. */
return both + src;
@ -210,6 +184,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
jobject bitmap;
int left, top, temp_y;
unsigned int prod, raster_y;
unsigned long foreground, back_pixel, rb;
if (!s->gc->num_clip_rects)
/* Clip region is empty. */
@ -219,6 +194,17 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
/* Nothing to draw. */
return;
/* Swizzle the foreground and background in s->gc into BGR, then add
an alpha channel. */
foreground = s->gc->foreground;
back_pixel = s->gc->background;
rb = foreground & 0x00ff00ff;
foreground &= ~0x00ff00ff;
foreground |= rb >> 16 | rb << 16 | 0xff000000;
rb = back_pixel & 0x00ff00ff;
back_pixel &= ~0x00ff00ff;
back_pixel |= rb >> 16 | rb << 16 | 0xff000000;
USE_SAFE_ALLOCA;
prepare_face_for_display (s->f, s->face);
@ -294,7 +280,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
+ stride * temp_y);
for (x = background.x; x < background.x + background.width; ++x)
row[x] = s->gc->background | 0xff000000;
row[x] = back_pixel;
}
}
@ -327,10 +313,9 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
{
prod
= sfntfont_android_scale32 (U255TO256 (raster_row[x]),
(s->gc->foreground
| 0xff000000));
foreground);
row[left + x]
= sfntfont_android_blendrgb (prod, row[left + x]);
= sfntfont_android_blend (prod, row[left + x]);
}
}
}

View file

@ -860,7 +860,7 @@ sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character,
/* Emacs missing charsets? */
return false;
font_character = ENCODE_CHAR (charset, XFIXNUM (character));
font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character));
if (font_character == CHARSET_INVALID_CODE (charset))
return false;