1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

Port visible bell to Android

* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Ignore
GC_INVERT.

* java/org/gnu/emacs/EmacsFillRectangle.java
(EmacsFillRectangle) <invertFilter>: New variable.
(perform): If the transfer mode is invert, copy the source
to itself with invertFilter as the color filter.

* java/org/gnu/emacs/EmacsGC.java (EmacsGC) <xorAlu, srcInAlu>:
Delete now-redundant ALUs.
(markDirty): Cease updating the paint's transfermode.

* java/org/gnu/emacs/EmacsSafThread.java (openDocument1): Fix
typo in documentation.

* src/android.c (android_blit_xor): Delete unused function.
(android_copy_area): Remove calls to unused blit functions.

* src/androidgui.h (enum android_gc_function): Rename XOR to
INVERT.

* src/androidterm.c (android_flash): Replace with GXinvert.
This commit is contained in:
Po Lu 2024-05-02 11:31:37 +08:00
parent d3e95fcae9
commit b84fa71f89
7 changed files with 62 additions and 374 deletions

View file

@ -22,13 +22,23 @@ package org.gnu.emacs;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.util.Log;
public final class EmacsDrawRectangle
{
private static final Xfermode srcInAlu;
static
{
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
};
public static void
perform (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height)
@ -40,8 +50,10 @@ public final class EmacsDrawRectangle
Canvas canvas;
Bitmap clipBitmap;
/* TODO implement stippling. */
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
/* TODO implement stippling for this request. */
if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED
/* And GC_INVERT also. */
|| gc.fill_style == EmacsGC.GC_INVERT)
return;
canvas = drawable.lockCanvas (gc);
@ -103,7 +115,7 @@ public final class EmacsDrawRectangle
/* Set the transfer mode to SRC_IN to preserve only the parts
of the source that overlap with the mask. */
maskPaint = new Paint ();
maskPaint.setXfermode (EmacsGC.srcInAlu);
maskPaint.setXfermode (srcInAlu);
maskPaint.setStyle (Paint.Style.STROKE);
/* Draw the source. */

View file

@ -21,6 +21,8 @@ package org.gnu.emacs;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
@ -28,30 +30,42 @@ import android.util.Log;
public final class EmacsFillRectangle
{
/* Color filter that inverts colors from the source. */
private static final ColorFilter invertFilter;
static
{
invertFilter = new ColorMatrixColorFilter (new float[] {
-1f, 0f, 0f, 0f, 255f,
0f, -1f, 0f, 0f, 255f,
0f, 0f, -1f, 0f, 255f,
0f, 0f, 0f, 1f, 0f,
});
};
public static void
perform (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height)
{
Paint maskPaint, paint;
Canvas maskCanvas;
Bitmap maskBitmap;
Paint paint;
Rect rect;
Rect maskRect, dstRect;
Canvas canvas;
Bitmap clipBitmap;
Bitmap invertBitmap;
canvas = drawable.lockCanvas (gc);
if (canvas == null)
/* Clip masks are not respected or implemented when specified with
this request. */
if (canvas == null || gc.clip_mask != null)
return;
rect = new Rect (x, y, x + width, y + height);
if (gc.function != EmacsGC.GC_INVERT)
{
paint = gc.gcPaint;
paint.setStyle (Paint.Style.FILL);
if (gc.clip_mask == null)
{
if (gc.fill_style != EmacsGC.GC_FILL_OPAQUE_STIPPLED)
canvas.drawRect (rect, paint);
else
@ -59,57 +73,17 @@ public final class EmacsFillRectangle
}
else
{
/* Drawing with a clip mask involves calculating the
intersection of the clip mask with the dst rect, and
extrapolating the corresponding part of the src rect. */
paint = new Paint ();
clipBitmap = gc.clip_mask.bitmap;
dstRect = new Rect (x, y, x + width, y + height);
maskRect = new Rect (gc.clip_x_origin,
gc.clip_y_origin,
(gc.clip_x_origin
+ clipBitmap.getWidth ()),
(gc.clip_y_origin
+ clipBitmap.getHeight ()));
if (!maskRect.setIntersect (dstRect, maskRect))
/* There is no intersection between the clip mask and the
dest rect. */
return;
/* Finally, create a temporary bitmap that is the size of
maskRect. */
maskBitmap
= Bitmap.createBitmap (maskRect.width (), maskRect.height (),
Bitmap.Config.ARGB_8888);
/* Draw the mask onto the maskBitmap. */
maskCanvas = new Canvas (maskBitmap);
maskRect.offset (-gc.clip_x_origin,
-gc.clip_y_origin);
maskCanvas.drawBitmap (gc.clip_mask.bitmap,
maskRect, new Rect (0, 0,
maskRect.width (),
maskRect.height ()),
paint);
maskRect.offset (gc.clip_x_origin,
gc.clip_y_origin);
/* Set the transfer mode to SRC_IN to preserve only the parts
of the source that overlap with the mask. */
maskPaint = new Paint ();
maskPaint.setXfermode (EmacsGC.srcInAlu);
/* Draw the source. */
maskCanvas.drawRect (maskRect, maskPaint);
/* Finally, draw the mask bitmap to the destination. */
paint.setXfermode (null);
canvas.drawBitmap (maskBitmap, null, maskRect, paint);
/* Recycle this unused bitmap. */
maskBitmap.recycle ();
/* Simply invert the destination, which is only implemented for
this request. As Android doesn't permit copying a bitmap to
itself, a copy of the source must be procured beforehand. */
invertBitmap = Bitmap.createBitmap (drawable.getBitmap (),
x, y, width, height);
paint.setColorFilter (invertFilter);
canvas.drawBitmap (invertBitmap, null, rect, paint);
paint.setColorFilter (null);
invertBitmap.recycle ();
}
drawable.damageRect (rect);

View file

@ -27,9 +27,7 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader.TileMode;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
@ -41,7 +39,7 @@ import android.os.Build;
public final class EmacsGC extends EmacsHandleObject
{
public static final int GC_COPY = 0;
public static final int GC_XOR = 1;
public static final int GC_INVERT = 1;
public static final int GC_FILL_SOLID = 0;
public static final int GC_FILL_OPAQUE_STIPPLED = 1;
@ -49,8 +47,6 @@ public final class EmacsGC extends EmacsHandleObject
public static final int GC_LINE_SOLID = 0;
public static final int GC_LINE_ON_OFF_DASH = 1;
public static final Xfermode xorAlu, srcInAlu;
public int function, fill_style;
public int foreground, background;
public int clip_x_origin, clip_y_origin;
@ -72,12 +68,6 @@ public final class EmacsGC extends EmacsHandleObject
rectangles changed. 0 if there are no clip rectangles. */
public long clipRectID;
static
{
xorAlu = new PorterDuffXfermode (Mode.XOR);
srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
}
/* The following fields are only set on immutable GCs. */
public
@ -131,8 +121,6 @@ public final class EmacsGC extends EmacsHandleObject
/* A line_width of 0 is equivalent to that of 1. */
gcPaint.setStrokeWidth (line_width < 1 ? 1 : line_width);
gcPaint.setColor (foreground | 0xff000000);
gcPaint.setXfermode (function == GC_XOR
? xorAlu : srcInAlu);
/* Update the stipple object with the new stipple bitmap, or delete
it if the stipple has been cleared on systems too old to support

View file

@ -1623,10 +1623,10 @@ public final class EmacsSafThread extends HandlerThread
mode is merely w.
This may be ascribed to a mix-up in Android's documentation
regardin DocumentsProvider: the `openDocument' function is only
documented to accept r or rw, whereas the default
implementation of the `openFile' function (which documents rwt)
delegates to `openDocument'. */
regarding DocumentsProvider: the `openDocument' function is only
documented to accept r or rw, whereas the default implementation
of the `openFile' function (which documents rwt) delegates to
`openDocument'. */
if (read && write && truncate && fileDescriptor != null
&& !EmacsNative.ftruncate (fileDescriptor.getFd ()))

View file

@ -4381,286 +4381,6 @@ android_blit_copy (int src_x, int src_y, int width, int height,
}
/* Xor a rectangle SRC_X, SRC_Y, WIDTH and HEIGHT from SRC, described
by SRC_INFO, to DST_X and DST_Y in DST, as described by DST_INFO.
Ignore the alpha channel when computing the exclusive-or of the
destination pixel.
If MASK is set, mask the source data using MASK_INFO, translating
it by GC->clip_x_origin and GC->clip_y_origin. MASK must be a
pixmap of depth 1.
N.B. that currently only copies between bitmaps of depth 24 are
implemented. */
static void
android_blit_xor (int src_x, int src_y, int width, int height,
int dst_x, int dst_y, struct android_gc *gc,
unsigned char *src, AndroidBitmapInfo *src_info,
unsigned char *dst, AndroidBitmapInfo *dst_info,
unsigned char *mask, AndroidBitmapInfo *mask_info)
{
#if 0
uintptr_t start, end;
int mask_offset;
size_t pixel, offset, offset1;
unsigned char *src_current, *dst_current;
unsigned char *mask_current;
int overflow, temp, i;
bool backwards;
unsigned int *long_src, *long_dst;
#endif /* 0 */
/* Note that this alu hasn't been tested -- it probably does not
work! */
emacs_abort ();
#if 0
/* Assert that the specified coordinates are within bounds. */
eassert (src_x >= 0 && src_y >= 0
&& dst_x >= 0 && dst_y >= 0);
eassert (src_x + width <= src_info->width);
eassert (src_y + height <= src_info->height);
eassert (dst_x + width <= dst_info->width);
eassert (dst_y + height <= dst_info->height);
/* Now check that each bitmap has the correct format. */
eassert (src_info->format == dst_info->format
&& src_info->format == ANDROID_BITMAP_FORMAT_RGBA_8888);
pixel = sizeof (unsigned int);
/* Android doesn't have A1 bitmaps, so A8 is used to represent
packed bitmaps of depth 1. */
eassert (!mask || mask_info->format == ANDROID_BITMAP_FORMAT_A_8);
/* Calculate the address of the first pixel of the first row to be
copied in both src and dst. Compare them to determine the
direction in which the copy is to take place. */
overflow = ckd_mul (&start, src_y, src_info->stride);
overflow |= ckd_mul (&end, src_x, pixel);
overflow |= ckd_add (&start, (uintptr_t) src, start);
if (overflow)
return;
src_current = (unsigned char *) start;
overflow = ckd_mul (&start, dst_y, src_info->stride);
overflow |= ckd_mul (&end, dst_x, pixel);
overflow |= ckd_add (&start, (uintptr_t) dst, start);
if (overflow)
return;
dst_current = (unsigned char *) start;
backwards = false;
/* Now see if copying should proceed from the bottom up. */
if (src == dst && dst_current >= src_current)
{
backwards = true;
/* Walk src and dst from bottom to top, in order to avoid
overlap. Calculate the coordinate of the last pixel of the
last row in both src and dst. */
overflow = ckd_mul (&start, src_y + height - 1,
src_info->stride);
if (mask) /* If a mask is set, put the pointers before the end
of the row. */
overflow |= ckd_mul (&end, src_x + width - 1, pixel);
else
overflow |= ckd_mul (&end, src_x, pixel);
overflow |= ckd_add (&start, start, end);
overflow |= ckd_add (&start, (uintptr_t) src, start);
if (overflow)
return;
src_current = (unsigned char *) start;
overflow = ckd_mul (&start, dst_y + height - 1,
dst_info->stride);
if (mask) /* If a mask is set, put the pointers before the end
of the row. */
overflow |= ckd_mul (&end, dst_x + width - 1, pixel);
else
overflow |= ckd_mul (&end, dst_x, pixel);
overflow |= ckd_add (&start, start, end);
overflow |= ckd_add (&start, (uintptr_t) dst, start);
if (overflow)
return;
dst_current = (unsigned char *) start;
}
if (!mask)
{
/* Change the direction of the copy depending on how SRC and DST
overlap. */
for (i = 0; i < height; ++i)
{
if (backwards)
{
for (i = width - 1; i <= 0; --i)
(((unsigned int *) dst_current)[i])
/* Keep the alpha channel intact. */
^= (((unsigned int *) src_current)[i]) & 0xffffff;
/* Proceed to the last row. */
src_current -= src_info->stride;
dst_current -= dst_info->stride;
}
else
{
for (i = 0; i < width; ++i)
(((unsigned int *) dst_current)[i])
/* Keep the alpha channel intact. */
^= (((unsigned int *) src_current)[i]) & 0xffffff;
/* Proceed to the next row. */
src_current += src_info->stride;
dst_current += dst_info->stride;
}
}
}
else
{
/* Adjust the source and destination Y. The start is MAX
(dst_y, gc->clip_y_origin); the difference between that value
and dst_y is the offset to apply to src_y. */
temp = dst_y;
dst_y = MAX (dst_y, gc->clip_y_origin);
src_y += dst_y - temp;
height -= dst_y - temp;
/* Verify that the bounds are correct. */
eassert (dst_y + height
<= gc->clip_y_origin + mask_info->height);
eassert (dst_y >= gc->clip_y_origin);
/* There is a mask. For each scan line... */
if (backwards)
{
/* Calculate the number of pixels at the end of the
mask. */
mask_offset = dst_x + width;
mask_offset -= mask_info->width + gc->clip_x_origin;
if (mask_info < 0)
mask_info = 0;
/* Calculate the last column of the mask that will be
consulted. */
temp = dst_x - gc->clip_x_origin;
temp += MIN (mask_info->width - temp,
width - mask_offset);
if (temp < 0)
return;
/* Now calculate the last row of the mask that will be
consulted. */
i = dst_y - gc->clip_y_origin + height;
/* Turn both into offsets. */
if (ckd_mul (&offset, temp, pixel)
|| ckd_mul (&offset1, i, mask_info->stride)
|| ckd_add (&offset, offset, offset1)
|| ckd_add (&start, (uintptr_t) mask, offset))
return;
mask = mask_current = (unsigned char *) start;
for (i = 0; i < height; ++i)
{
/* Skip backwards past the end of the mask. */
long_src = (unsigned int *) (src_current - mask_offset * pixel);
long_dst = (unsigned int *) (dst_current - mask_offset * pixel);
mask = mask_current;
/* For each pixel covered by the mask... */
temp = MIN (mask_info->width - temp, width - mask_offset);
while (temp--)
/* XOR the source to the destination, masked by the
mask. */
*long_dst-- ^= ((*(long_src--) & (0u - (*(mask--) & 1)))
& 0xffffff);
/* Return to the last row. */
src_current -= src_info->stride;
dst_current -= dst_info->stride;
mask_current -= mask_info->stride;
}
}
else
{
/* Calculate the first column of the mask that will be
consulted. */
mask_offset = dst_x - gc->clip_x_origin;
/* Adjust the mask by that much. */
if (mask_offset > 0)
mask += mask_offset;
else
{
/* Offset src and dst by the mask offset. */
src_current += -mask_offset * pixel;
dst_current += -mask_offset * pixel;
width -= mask_offset;
}
/* Now move mask to the position of the first row. */
mask += gc->clip_y_origin * mask_info->stride;
for (i = 0; i < height; ++i)
{
long_src = (unsigned int *) src_current;
long_dst = (unsigned int *) dst_current;
mask_current = mask;
if (mask_offset > 0)
{
/* Copy bytes according to the mask. */
temp = MIN (mask_info->width - mask_offset, width);
while (temp--)
*long_dst++ ^= ((*(long_src++)
& (0u - (*(mask_current++) & 1)))
& 0xffffff);
}
else
{
/* Copy bytes according to the mask. */
temp = MIN (mask_info->width, width);
while (temp--)
*long_dst++ = ((*(long_src++)
& (0u - (*(mask_current++) & 1)))
& 0xffffff);
}
src_current += src_info->stride;
dst_current += dst_info->stride;
mask += mask_info->stride;
}
}
}
#endif /* 0 */
}
void
android_copy_area (android_drawable src, android_drawable dest,
struct android_gc *gc, int src_x, int src_y,
@ -4763,10 +4483,10 @@ android_copy_area (android_drawable src, android_drawable dest,
do_blit = android_blit_copy;
break;
case ANDROID_GC_XOR:
do_blit = android_blit_xor;
break;
/* case ANDROID_GC_INVERT: */
/* do_blit = android_blit_invert; */
/* A GC with its operation set to ANDROID_GC_INVERT is never given
to CopyArea. */
default:
emacs_abort ();
}

View file

@ -56,7 +56,7 @@ struct android_point
enum android_gc_function
{
ANDROID_GC_COPY = 0,
ANDROID_GC_XOR = 1,
ANDROID_GC_INVERT = 1,
};
enum android_gc_value_mask

View file

@ -151,14 +151,8 @@ android_flash (struct frame *f)
fd_set fds;
block_input ();
values.function = ANDROID_GC_XOR;
values.foreground = (FRAME_FOREGROUND_PIXEL (f)
^ FRAME_BACKGROUND_PIXEL (f));
gc = android_create_gc ((ANDROID_GC_FUNCTION
| ANDROID_GC_FOREGROUND),
&values);
values.function = ANDROID_GC_INVERT;
gc = android_create_gc (ANDROID_GC_FUNCTION, &values);
/* Get the height not including a menu bar widget. */
int height = FRAME_PIXEL_HEIGHT (f);