From 7fdde02f3216536aa8977fa4b396bec8fbaf994b Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 29 May 2023 17:46:19 +0800 Subject: [PATCH] Work around more problems with Bitmaps * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function `blitRect'. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): Use it on Android 8.x. * src/android.c (blitRect): Implement new function. --- java/org/gnu/emacs/EmacsNative.java | 10 +++ java/org/gnu/emacs/EmacsSurfaceView.java | 33 +++++++- src/android.c | 98 ++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index e699dda9ad4..56c03ee38dc 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -20,6 +20,9 @@ along with GNU Emacs. If not, see . */ package org.gnu.emacs; import android.content.res.AssetManager; + +import android.graphics.Bitmap; + import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -216,6 +219,13 @@ public final class EmacsNative failure. */ public static native int[] getSelection (short window); + + /* Graphics functions used as a replacement for potentially buggy + Android APIs. */ + + public static native void blitRect (Bitmap src, Bitmap dest, int x1, + int y1, int x2, int y2); + static { /* Older versions of Android cannot link correctly with shared diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index e0411f7f8b3..0deb930c2c2 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -57,11 +57,36 @@ public final class EmacsSurfaceView extends View private void copyToFrontBuffer (Bitmap bitmap, Rect damageRect) { - if (damageRect != null) - bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect, - bitmapPaint); + EmacsService.checkEmacsThread (); + + if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O + && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) + { + /* If `drawBitmap' can safely be used while a bitmap is locked + by another thread, continue here... */ + + if (damageRect != null) + bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect, + bitmapPaint); + else + bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint); + } else - bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint); + { + /* But if it can not, as on Android 8.0 and 8.1, then use a + replacement function. */ + + if (damageRect != null) + EmacsNative.blitRect (bitmap, frontBuffer, + damageRect.left, + damageRect.top, + damageRect.right, + damageRect.bottom); + else + EmacsNative.blitRect (bitmap, frontBuffer, 0, 0, + bitmap.getWidth (), + bitmap.getHeight ()); + } } private void diff --git a/src/android.c b/src/android.c index 7b9c478f212..4184be3086b 100644 --- a/src/android.c +++ b/src/android.c @@ -4754,6 +4754,104 @@ android_copy_area (android_drawable src, android_drawable dest, +JNIEXPORT void JNICALL +NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, + jobject src, jobject dest, + jint x1, jint y1, jint x2, jint y2) +{ + AndroidBitmapInfo src_info, dest_info; + unsigned char *src_data_1, *dest_data_1; + void *src_data, *dest_data; + + /* N.B. that X2 and Y2 represent the pixel past the edge of the + rectangle; thus, the width is x2 - x1 and the height is y2 - + y1. */ + + memset (&src_info, 0, sizeof src_info); + memset (&dest_info, 0, sizeof dest_info); + AndroidBitmap_getInfo (env, src, &src_info); + AndroidBitmap_getInfo (env, dest, &dest_info); + + /* If the stride is 0 after a call to `getInfo', assume it + failed. */ + + if (!src_info.stride || !dest_info.stride) + return; + + /* If formats differ, abort. */ + eassert (src_info.format == dest_info.format + && src_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); + + /* Lock the image data. */ + src_data = NULL; + AndroidBitmap_lockPixels (env, src, &src_data); + + if (!src_data) + return; + + dest_data = NULL; + AndroidBitmap_lockPixels (env, dest, &dest_data); + + if (!dest_data) + goto fail1; + + /* Now clip the rectangle to the bounds of the source and + destination bitmap. */ + + x1 = MAX (x1, 0); + y1 = MAX (y1, 0); + x2 = MAX (x2, 0); + y2 = MAX (y2, 0); + + + if (x1 >= src_info.width + || x1 >= dest_info.width) + x1 = MIN (dest_info.width - 1, src_info.width - 1); + + if (x2 > src_info.width + || x2 > dest_info.width) + x2 = MIN (src_info.width, dest_info.width); + + if (y1 >= src_info.height + || y1 >= dest_info.height) + y1 = MIN (dest_info.height - 1, src_info.height - 1); + + if (y2 > src_info.height + || y2 > dest_info.height) + y2 = MIN (src_info.height, dest_info.height); + + if (x1 >= x2 || y1 >= y2) + goto fail2; + + /* Determine the address of the first line to copy. */ + + src_data_1 = src_data; + dest_data_1 = dest_data; + src_data_1 += x1 * 4; + src_data_1 += y1 * src_info.stride; + dest_data_1 += x1 * 4; + dest_data_1 += y1 * dest_info.stride; + + /* Start copying each line. */ + + while (y1 != y2) + { + memcpy (dest_data_1, src_data_1, (x2 - x1) * 4); + src_data_1 += src_info.stride; + dest_data_1 += dest_info.stride; + y1++; + } + + /* Complete the copy and unlock the bitmap. */ + + fail2: + AndroidBitmap_unlockPixels (env, dest); + fail1: + AndroidBitmap_unlockPixels (env, src); +} + + + void android_free_pixmap (android_pixmap pixmap) { -- 2.39.2