From: Po Lu Date: Mon, 29 May 2023 07:44:14 +0000 (+0800) Subject: Implement android_copy_area in C X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=00671b18438fec8f2e7f774cb3fd4cd6f694fd18;p=emacs.git Implement android_copy_area in C * java/org/gnu/emacs/EmacsCopyArea.java: Remove file. * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea): Delete function. * src/android.c (struct android_emacs_service) (android_init_emacs_service): Remove `copy_area'. (android_create_gc, android_change_gc, android_get_gc_values): Record new GC values. (android_neon_mask_line): New function. (android_blit_copy, android_blit_xor): New functions. (android_copy_area): Implement in C. (android_lock_bitmap): Accept drawables instead of windows. * src/android.h: Adjust prototype for `android_lock_bitmap'. * src/androidgui.h (struct android_gc): Record last known GC values. --- diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java deleted file mode 100644 index f69b0cde866..00000000000 --- a/java/org/gnu/emacs/EmacsCopyArea.java +++ /dev/null @@ -1,206 +0,0 @@ -/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- - -Copyright (C) 2023 Free Software Foundation, Inc. - -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 . */ - -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.Xfermode; - -public final class EmacsCopyArea -{ - private static Xfermode overAlu; - - static - { - overAlu = new PorterDuffXfermode (Mode.SRC_OVER); - }; - - private static void - insetRectBy (Rect rect, int left, int top, int right, - int bottom) - { - rect.left += left; - rect.top += top; - rect.right -= right; - rect.bottom -= bottom; - } - - public static void - perform (EmacsDrawable source, EmacsGC gc, - EmacsDrawable destination, - int src_x, int src_y, int width, int height, - int dest_x, int dest_y) - { - int i; - Bitmap bitmap; - Paint maskPaint, paint; - Canvas maskCanvas, canvas; - Bitmap srcBitmap, maskBitmap, clipBitmap; - Rect rect, maskRect, srcRect, dstRect, maskDestRect; - boolean needFill; - - /* TODO implement stippling. */ - if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) - return; - - paint = gc.gcPaint; - - canvas = destination.lockCanvas (gc); - - if (canvas == null) - return; - - /* A copy must be created or drawBitmap could end up overwriting - itself. */ - srcBitmap = source.getBitmap (); - - /* If srcBitmap is out of bounds, then adjust the source rectangle - to be within bounds. Note that tiling on windows with - backgrounds is unimplemented. */ - - if (src_x < 0) - { - width += src_x; - dest_x -= src_x; - src_x = 0; - } - - if (src_y < 0) - { - height += src_y; - dest_y -= src_y; - src_y = 0; - } - - if (src_x + width > srcBitmap.getWidth ()) - width = srcBitmap.getWidth () - src_x; - - if (src_y + height > srcBitmap.getHeight ()) - height = srcBitmap.getHeight () - src_y; - - /* If width and height are empty or negative, then skip the entire - CopyArea operation lest createBitmap throw an exception. */ - - if (width <= 0 || height <= 0) - return; - - rect = new Rect (dest_x, dest_y, dest_x + width, - dest_y + height); - - if (gc.clip_mask == null) - { - if (source == destination) - { - /* Create a copy of the bitmap, since Android can't handle - overlapping copies. */ - bitmap = Bitmap.createBitmap (srcBitmap, - src_x, src_y, width, - height); - canvas.drawBitmap (bitmap, null, rect, paint); - bitmap.recycle (); - } - else - { - /* But here the bitmaps are known to not overlap, so avoid - that extra consing overhead. */ - - srcRect = new Rect (src_x, src_y, src_x + width, - src_y + height); - canvas.drawBitmap (srcBitmap, srcRect, rect, paint); - } - } - 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. */ - clipBitmap = gc.clip_mask.bitmap; - dstRect = new Rect (dest_x, dest_y, - dest_x + width, - dest_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 ())); - clipBitmap = gc.clip_mask.bitmap; - - if (!maskRect.setIntersect (dstRect, maskRect)) - /* There is no intersection between the clip mask and the - dest rect. */ - return; - - /* Now figure out which part of the source corresponds to - maskRect and return it relative to srcBitmap. */ - srcRect = new Rect (src_x, src_y, src_x + width, - src_y + height); - insetRectBy (srcRect, maskRect.left - dstRect.left, - maskRect.top - dstRect.top, - maskRect.right - dstRect.right, - maskRect.bottom - dstRect.bottom); - - /* 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); - maskPaint = new Paint (); - 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 ()), - maskPaint); - 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.setXfermode (EmacsGC.srcInAlu); - - /* Draw the source. */ - maskDestRect = new Rect (0, 0, srcRect.width (), - srcRect.height ()); - maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect, - maskPaint); - - /* Finally, draw the mask bitmap to the destination. */ - paint.setXfermode (overAlu); - canvas.drawBitmap (maskBitmap, null, maskRect, paint); - gc.resetXfermode (); - - /* Recycle this unused bitmap. */ - maskBitmap.recycle (); - } - - destination.damageRect (rect); - } -} diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d2e52ed5e62..546d22627c5 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -374,18 +374,6 @@ public final class EmacsService extends Service EmacsDrawPoint.perform (drawable, gc, x, y); } - public void - copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable, - EmacsGC gc, - int srcX, int srcY, int width, int height, int destX, - int destY) - { - checkEmacsThread (); - EmacsCopyArea.perform (srcDrawable, gc, dstDrawable, - srcX, srcY, width, height, destX, - destY); - } - public void clearWindow (EmacsWindow window) { diff --git a/src/android.c b/src/android.c index 8a41a7cdec5..33d766a90d9 100644 --- a/src/android.c +++ b/src/android.c @@ -28,6 +28,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include @@ -68,6 +69,10 @@ bool android_init_gui; #include +#ifdef __aarch64__ +#include +#endif /* __aarch64__ */ + #define ANDROID_THROW(env, class, msg) \ ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) @@ -95,7 +100,6 @@ struct android_emacs_service jmethodID draw_rectangle; jmethodID draw_line; jmethodID draw_point; - jmethodID copy_area; jmethodID clear_window; jmethodID clear_area; jmethodID ring_bell; @@ -2178,10 +2182,6 @@ android_init_emacs_service (void) FIND_METHOD (draw_point, "drawPoint", "(Lorg/gnu/emacs/EmacsDrawable;" "Lorg/gnu/emacs/EmacsGC;II)V"); - FIND_METHOD (copy_area, "copyArea", - "(Lorg/gnu/emacs/EmacsDrawable;" - "Lorg/gnu/emacs/EmacsDrawable;" - "Lorg/gnu/emacs/EmacsGC;IIIIII)V"); FIND_METHOD (clear_window, "clearWindow", "(Lorg/gnu/emacs/EmacsWindow;)V"); FIND_METHOD (clear_area, "clearArea", @@ -3356,6 +3356,16 @@ android_create_gc (enum android_gc_value_mask mask, /* This means to not apply any clipping. */ gc->num_clip_rects = -1; + /* Apply the other default values. */ + gc->function = ANDROID_GC_COPY; + gc->fill_style = ANDROID_FILL_SOLID; + gc->clip_x_origin = 0; + gc->clip_y_origin = 0; + gc->clip_mask = ANDROID_NONE; + gc->stipple = ANDROID_NONE; + gc->ts_x_origin = 0; + gc->ts_y_origin = 0; + if (!gc->gcontext) { xfree (gc); @@ -3430,10 +3440,13 @@ android_change_gc (struct android_gc *gc, } if (mask & ANDROID_GC_FUNCTION) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_function, - values->function); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_function, + values->function); + gc->function = values->function; + } if (mask & ANDROID_GC_CLIP_X_ORIGIN) { @@ -3441,6 +3454,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_x_origin, values->clip_x_origin); + gc->clip_x_origin = values->clip_x_origin; clip_changed = true; } @@ -3450,6 +3464,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_y_origin, values->clip_y_origin); + gc->clip_y_origin = values->clip_y_origin; clip_changed = true; } @@ -3461,6 +3476,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_mask, what); + gc->clip_mask = values->clip_mask; /* Changing GCClipMask also clears the clip rectangles. */ (*android_java_env)->SetObjectField (android_java_env, @@ -3482,25 +3498,35 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_stipple, what); + gc->stipple = values->stipple; } if (mask & ANDROID_GC_FILL_STYLE) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_fill_style, - values->fill_style); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_fill_style, + values->fill_style); + gc->fill_style = values->fill_style; + } if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_x, - values->ts_x_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_ts_origin_x, + values->ts_x_origin); + gc->ts_x_origin = values->ts_x_origin; + } if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_y, - values->ts_y_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_ts_origin_y, + values->ts_y_origin); + gc->ts_y_origin = values->ts_y_origin; + } if (mask) { @@ -3732,22 +3758,13 @@ android_get_gc_values (struct android_gc *gc, values->background = gc->background; if (mask & ANDROID_GC_FUNCTION) - values->function - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_function); + values->function = gc->function; if (mask & ANDROID_GC_CLIP_X_ORIGIN) - values->clip_x_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_clip_x_origin); + values->clip_x_origin = gc->clip_x_origin; if (mask & ANDROID_GC_CLIP_Y_ORIGIN) - values->clip_y_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_clip_y_origin); + values->clip_y_origin = gc->clip_y_origin; if (mask & ANDROID_GC_FILL_STYLE) values->fill_style @@ -3913,34 +3930,827 @@ android_set_fill_style (struct android_gc *gc, android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv); } + + +/* Pixmap bit blit implementation. This exists as `Canvas.drawBitmap' + seems to have trouble with copying bitmap data from one bitmap back + to itself on Android 8.0. */ + +/* Function called to actually perform the copy. */ + +typedef void (*android_blit_func) (int, int, int, int, int, int, + struct android_gc *, + unsigned char *, AndroidBitmapInfo *, + unsigned char *, AndroidBitmapInfo *, + unsigned char *, AndroidBitmapInfo *); + + + +#ifdef __aarch64__ + +/* Copy N pixels from SRC to DST, using MASK as a depth 1 clip + mask. */ + +static void +android_neon_mask_line (unsigned int *src, unsigned int *dst, + unsigned char *mask, int n) +{ + uint32x4_t src_low, src_high, dst_low, dst_high; + int16x8_t vmask; + int32x4_t ext_mask_low, ext_mask_high, low, high; + int rem; + + /* Calculate the remainder. */ + rem = n & 7; + + /* Process eight pixels at a time. */ + + if (n -= rem) + { + again: + /* Load the low and high four pixels from the source. */ + src_low = vld1q_u32 (src); + src_high = vld1q_u32 (src + 4); + + /* Do the same with the destination. */ + dst_low = vld1q_u32 (dst); + dst_high = vld1q_u32 (dst + 4); + + /* Load and sign extend the mask. */ + vmask = vmovl_s8 (vld1_u8 (mask)); + ext_mask_low = vmovl_s16 (vget_low_s16 (vmask)); + ext_mask_high = vmovl_s16 (vget_high_s16 (vmask)); + + /* Reinterpret the mask. */ + low = vreinterpretq_u32_s32 (ext_mask_low); + high = vreinterpretq_u32_s32 (ext_mask_high); + + /* Apply the mask. */ + dst_low = vbicq_u32 (dst_low, low); + src_low = vandq_u32 (src_low, low); + dst_high = vbicq_u32 (dst_high, high); + src_high = vandq_u32 (src_high, high); + + /* Write the result after combining both masked vectors. */ + vst1q_u32 (dst, vorrq_u32 (dst_low, src_low)); + vst1q_u32 (dst + 4, vorrq_u32 (dst_high, src_high)); + + /* Adjust src, dst and mask. */ + dst += 8; + src += 8; + mask += 8; + + /* See if this loop should continue. */ + n -= 8; + if (n > 0) + goto again; + } + + /* Process the remaining pixels. */ + + while (--rem) + { + /* Sign extend the mask. */ + n = *(signed char *) mask++; + + /* Combine src and dst. */ + *dst = ((*src & n) | (*dst & ~n)); + src++, dst++; + } +} + +#endif /* __aarch64__ */ + + + +/* Copy 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. + + 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. */ + +void +android_blit_copy (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) +{ + 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, xdir; + bool backwards; + unsigned int *long_src, *long_dst; + + /* 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, end, start); + overflow |= ckd_add (&start, (uintptr_t) src, start); + + if (overflow) + return; + + src_current = (unsigned char *) start; + + overflow = ckd_mul (&start, dst_y, dst_info->stride); + overflow |= ckd_mul (&end, dst_x, pixel); + overflow |= ckd_add (&start, end, start); + 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) + { + memmove (dst_current, src_current, + width * pixel); + + if (backwards) + { + /* Proceed to the last row. */ + src_current -= src_info->stride; + dst_current -= dst_info->stride; + } + else + { + /* 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_offset < 0) + mask_offset = 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 (INT_MULTIPLY_WRAPV (temp, pixel, &offset) + || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1) + || INT_ADD_WRAPV (offset, offset1, &offset) + || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start)) + return; + + mask = mask_current = (unsigned char *) start; + + while (--height) + { + /* 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--) + { + /* Copy the destination it to the source, masked by + the mask. */ + + /* Sign extend the mask. */ + i = *(signed char *) mask--; + + /* Apply the mask. */ + *long_dst = ((*long_src & i) | (*long_dst & ~i)); + + long_dst--; + long_src--; + } + + /* 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; + } + + /* Make sure it's not out of bounds. */ + + eassert (dst_y - gc->clip_y_origin >= 0); + if ((dst_y - gc->clip_y_origin) + height > mask_info->height) + return; + + /* Now move mask to the position of the first row. */ + + mask += ((dst_y - gc->clip_y_origin) + * mask_info->stride); + + /* Determine how many bytes need to be copied. */ + + if (mask_offset > 0) + temp = MIN (mask_info->width - mask_offset, width); + else + temp = MIN (mask_info->width, width); + + /* Copy bytes according to the mask. */ + + while (--height) + { + long_src = (unsigned int *) src_current; + long_dst = (unsigned int *) dst_current; + mask_current = mask; + +#ifndef __aarch64__ + while (temp--) + { + /* Sign extend the mask. */ + height = *(signed char *) mask_current++; + + /* Apply the mask. */ + *long_dst = ((*long_src & height) + | (*long_dst & ~height)); + } +#else /* __aarch64__ */ + android_neon_mask_line (long_src, long_dst, mask, temp); +#endif /* __aarch64__ */ + + src_current += src_info->stride; + dst_current += dst_info->stride; + mask += mask_info->stride; + } + } + } +} + + +/* 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. */ + +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) +{ + 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, xdir; + bool backwards; + unsigned int *long_src, *long_dst; + + /* 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 (INT_MULTIPLY_WRAPV (temp, pixel, &offset) + || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1) + || INT_ADD_WRAPV (offset, offset1, &offset) + || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start)) + 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, unsigned int width, unsigned int height, int dest_x, int dest_y) { - jobject src_object, dest_object, gcontext; + jobject src_object, dest_object, mask; + android_blit_func do_blit; + AndroidBitmapInfo src_info, dest_info, mask_info; + void *src_data, *dest_data, *mask_data; + int n_clip_rects, i; + bool flag; + struct android_rectangle bounds, rect, temp, *clip_rectangles; - src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW, - ANDROID_HANDLE_PIXMAP); - dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW, - ANDROID_HANDLE_PIXMAP); - gcontext = android_resolve_handle (gc->gcontext, - ANDROID_HANDLE_GCONTEXT); + /* Perform the copy. Loop over each clip rectangle, unless none are + set. Also, obtain bitmaps for src and dst, and possibly the mask + as well if it is present. */ - (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, - emacs_service, - service_class.class, - service_class.copy_area, - src_object, - dest_object, - gcontext, - (jint) src_x, (jint) src_y, - (jint) width, (jint) height, - (jint) dest_x, (jint) dest_y); - android_exception_check (); + src_data = android_lock_bitmap (src, &src_info, &src_object); + if (!src_data) + return; + + mask_data = mask = NULL; + + if (src != dest) + { + dest_data = android_lock_bitmap (dest, &dest_info, &dest_object); + if (!dest_data) + goto fail; + } + else + { + dest_data = src_data; + dest_info = src_info; + } + + /* Obtain the bitmap for the mask if necessary. */ + + if (gc->clip_mask) + { + mask_data = android_lock_bitmap (gc->clip_mask, + &mask_info, &mask); + if (!mask_data) + goto fail1; + } + + /* Calculate the number of clip rectangles. */ + n_clip_rects = gc->num_clip_rects; + + /* If n_clip_rects is -1, then no clipping is in effect. Set rect + to the bounds of the destination. */ + + flag = n_clip_rects == -1; + if (flag) + { + n_clip_rects = 1; + clip_rectangles = ▭ + } + else if (!n_clip_rects) + goto fail2; + else + clip_rectangles = gc->clip_rects; + + /* Set rect to the bounds of the destination. */ + + rect.x = 0; + rect.y = 0; + rect.width = dest_info.width; + rect.height = dest_info.height; + + if (mask_data) + { + /* Clip width and height to that of the mask. */ + + if (src_x + width > mask_info.width) + width = mask_info.width - src_x; + + if (src_y + height > mask_info.height) + height = mask_info.height - src_y; + } + + /* Clip width and height to that of the source. */ + + if (src_x + width > src_info.width) + width = src_info.width - src_x; + + if (src_y + height > src_info.height) + height = src_info.height - src_y; + + /* Return if the copy is outside the source. */ + + if (width <= 0 || height <= 0) + goto fail2; + + /* Look up the right function for the alu. */ + + switch (gc->function) + { + case ANDROID_GC_COPY: + do_blit = android_blit_copy; + break; + + case ANDROID_GC_XOR: + do_blit = android_blit_xor; + break; + } + + /* Load the bounds of the destination rectangle. */ + bounds.x = dest_x; + bounds.y = dest_y; + bounds.width = width; + bounds.height = height; + + /* For each clip rectangle... */ + for (i = 0; i < n_clip_rects; ++i) + { + /* Calculate its intersection with the destination + rectangle. */ + + if (!gui_intersect_rectangles (&clip_rectangles[i], &bounds, + &temp)) + continue; + + /* And that of the destination itself. */ + + if (!flag && !gui_intersect_rectangles (&temp, &rect, &temp)) + continue; + + /* Now perform the copy. */ + (*do_blit) (src_x + temp.x - dest_x, /* temp.x relative to src_x */ + src_y + temp.y - dest_y, /* temp.y relative to src_y */ + temp.width, /* Width of area to copy. */ + temp.height, /* Height of area to copy. */ + temp.x, temp.y, /* Coordinates to copy to. */ + gc, /* GC. */ + src_data, &src_info, /* Source drawable. */ + dest_data, &dest_info, /* Destination drawable. */ + mask_data, &mask_info); /* Mask drawable. */ + } + + /* Now damage the destination drawable accordingly, should it be a + window. */ + + if (android_handles[dest].type == ANDROID_HANDLE_WINDOW) + android_damage_window (dest, &bounds); + + fail2: + if (mask) + { + AndroidBitmap_unlockPixels (android_java_env, mask); + ANDROID_DELETE_LOCAL_REF (mask); + } + fail1: + if (src != dest) + { + AndroidBitmap_unlockPixels (android_java_env, dest_object); + ANDROID_DELETE_LOCAL_REF (dest_object); + } + fail: + AndroidBitmap_unlockPixels (android_java_env, src_object); + ANDROID_DELETE_LOCAL_REF (src_object); } + + void android_free_pixmap (android_pixmap pixmap) { @@ -4815,26 +5625,27 @@ android_wc_lookup_string (android_key_pressed_event *event, /* Low level drawing primitives. */ -/* Lock the bitmap corresponding to the window WINDOW. Return the +/* Lock the bitmap corresponding to the drawable DRAWABLE. Return the bitmap data upon success, and store the bitmap object in BITMAP_RETURN. Value is NULL upon failure. The caller must take care to unlock the bitmap data afterwards. */ unsigned char * -android_lock_bitmap (android_window window, +android_lock_bitmap (android_window drawable, AndroidBitmapInfo *bitmap_info, jobject *bitmap_return) { - jobject drawable, bitmap; + jobject object, bitmap; void *data; - drawable = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + object = android_resolve_handle2 (drawable, ANDROID_HANDLE_WINDOW, + ANDROID_HANDLE_PIXMAP); /* Look up the drawable and get the bitmap corresponding to it. Then, lock the bitmap's bits. */ bitmap = (*android_java_env)->CallObjectMethod (android_java_env, - drawable, + object, drawable_class.get_bitmap); if (!bitmap) /* NULL is returned when the bitmap does not currently exist due diff --git a/src/android.h b/src/android.h index 62d420d4cce..d0440259161 100644 --- a/src/android.h +++ b/src/android.h @@ -68,7 +68,7 @@ enum android_handle_type extern jobject android_resolve_handle (android_handle, enum android_handle_type); -extern unsigned char *android_lock_bitmap (android_window, +extern unsigned char *android_lock_bitmap (android_drawable, AndroidBitmapInfo *, jobject *); extern void android_damage_window (android_window, diff --git a/src/androidgui.h b/src/androidgui.h index 6db25098398..02cc73809b9 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -137,6 +137,21 @@ struct android_gc /* Current background color. */ unsigned long background; + + /* The function. */ + enum android_gc_function function; + + /* The fill style. */ + enum android_fill_style fill_style; + + /* The clip X and Y origin. */ + int clip_x_origin, clip_y_origin; + + /* The clip mask image and stipple. */ + android_pixmap clip_mask, stipple; + + /* The tile-stipple X and Y origins. */ + int ts_x_origin, ts_y_origin; }; enum android_swap_action