]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement face stipples on Android
authorPo Lu <luangruo@yahoo.com>
Tue, 23 Apr 2024 06:30:38 +0000 (14:30 +0800)
committerEshel Yaron <me@eshelyaron.com>
Tue, 23 Apr 2024 10:13:55 +0000 (12:13 +0200)
* .gitignore:

* java/Makefile.in: Fix typos.

* java/org/gnu/emacs/EmacsFillRectangle.java (perform): Call
blitOpaqueStipple if filling an unobscured rectangle with an
opaque stipple.

* java/org/gnu/emacs/EmacsGC.java (EmacsGC) <tileObject>: New
field.
(markDirty): Synchronize the current stipple with tileObject.
(prepareStipple, blitOpaqueStipple): New functions.

* java/org/gnu/emacs/EmacsService.java (EmacsService)
<resources>: New static field.
(onCreate): Set it.

* src/android.c (android_create_bitmap_from_data): Correct order
of arguments to android_create_pixmap_from_bitmap_data.
(HAS_BUILTIN_TRAP): Delete macro.
(emacs_abort): Always induce backtraces by means of a NULL
pointer deference.

* src/dispextern.h (Emacs_GC, Emacs_Rectangle, GCForeground)
(GCBackground, GCFillStyle, GCStipple, FillOpaqueStipple)
[HAVE_ANDROID]: Define to their Android counterparts rather
than simulating their existence.

* src/epaths.in: Set bitmap path to /assets/bitmaps on Android.

* src/image.c (image_bitmap_pixmap): Also enable when
HAVE_ANDROID.

* src/sfntfont-android.c (sfntfont_android_put_glyphs): Assert
that this is never called to draw a stippled background.
* src/xfaces.c (x_create_gc) [HAVE_ANDROID]: Redefine as
wrapper around android_create_gc.
(prepare_face_for_display) [HAVE_ANDROID]: Enable stipples.

(cherry picked from commit b9c191d690fd5d1480858469df23cc4509996fae)

.gitignore
java/Makefile.in
java/org/gnu/emacs/EmacsFillRectangle.java
java/org/gnu/emacs/EmacsGC.java
java/org/gnu/emacs/EmacsService.java
src/android.c
src/dispextern.h
src/epaths.in
src/image.c
src/sfntfont-android.c
src/xfaces.c

index 4098e2210b5c5fb77c874c1a8885a18870158858..1557c085fad56635a1a99a5baa45276088fef138 100644 (file)
@@ -68,7 +68,7 @@ java/org/gnu/emacs/R.java
 
 # Built by `make'.
 java/org/gnu/emacs/EmacsConfig.java
-java/org/gnu/emacs/cf-stamp
+java/cf-stamp
 
 # Built by `config.status'.
 java/AndroidManifest.xml
index abddae6b5cfd4568d31b77716044397737d4b2a6..35d2637837cb858dcd27c3fc3a5cd43476084415 100644 (file)
@@ -197,10 +197,13 @@ install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
        $(AM_V_SILENT) mkdir -p install_temp/assets/etc
        $(AM_V_SILENT) mkdir -p install_temp/assets/lisp
        $(AM_V_SILENT) mkdir -p install_temp/assets/info
-# Install architecture independents to assets/etc and assets/lisp
+       $(AM_V_SILENT) mkdir -p install_temp/assets/bitmaps
+# Install architecture independents to assets/etc, assets/lisp and
+# assets/bitmaps
        $(AM_V_SILENT) cp -r $(top_srcdir)/lisp install_temp/assets
        $(AM_V_SILENT) cp -r $(top_srcdir)/etc install_temp/assets
        $(AM_V_SILENT) cp -r $(top_srcdir)/info install_temp/assets
+       $(AM_V_SILENT) cp -r $(top_srcdir)/src/bitmaps install_temp/assets
 # Replace etc/DOC generated by compiling Emacs for the build machine
 # with etc/DOC from the cross-compiled Emacs.
        $(AM_V_SILENT) test -f $(top_builddir)/cross/etc/DOC    \
@@ -354,8 +357,8 @@ public static final String[] EMACS_SHARED_LIBRARIES\
 
 # cf-stamp-1 is a phony target invoked in a second `make' instance after
 # all shared libraries are compiled, because the computation of
-# ALL_DEPENDENCIES cannot be postponed until that stage in this instance
-# of Make.
+# ALL_DEPENDENCIES in this instance of Make cannot be postponed until
+# that stage.
 cf-stamp: $(NDK_BUILD_SHARED) $(CROSS_LIBS)
        $(AM_V_EMACSCONFIG) $(MAKE) cf-stamp-1
        $(AM_V_at) touch $@
index ca87c06c01468ac291fa206fcdeef27e988cb890..f338a54f97b110e52376ce0bb225c249f407e1d6 100644 (file)
@@ -40,22 +40,23 @@ public final class EmacsFillRectangle
     Canvas canvas;
     Bitmap clipBitmap;
 
-    /* TODO implement stippling.  */
-    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
-      return;
-
     canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
-    paint = gc.gcPaint;
     rect = new Rect (x, y, x + width, y + height);
 
+    paint = gc.gcPaint;
     paint.setStyle (Paint.Style.FILL);
 
     if (gc.clip_mask == null)
-      canvas.drawRect (rect, paint);
+      {
+       if (gc.fill_style != EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+         canvas.drawRect (rect, paint);
+       else
+         gc.blitOpaqueStipple (canvas, rect);
+      }
     else
       {
        /* Drawing with a clip mask involves calculating the
@@ -113,4 +114,4 @@ public final class EmacsFillRectangle
 
     drawable.damageRect (rect);
   }
-}
+};
index e45f0666fe22dbc079a0ce3a90a349c19001792e..96df0c61ca613b4fd433f78c9f5c681132620d70 100644 (file)
@@ -22,10 +22,19 @@ package org.gnu.emacs;
 import android.graphics.Rect;
 import android.graphics.Paint;
 
+import android.graphics.Bitmap;
+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;
+
+import android.os.Build;
+
 /* X like graphics context structures.  Keep the enums in synch with
    androidgui.h! */
 
@@ -47,6 +56,9 @@ public final class EmacsGC extends EmacsHandleObject
   public EmacsPixmap clip_mask, stipple;
   public Paint gcPaint;
 
+  /* Drawable object for rendering the stiple bitmap.  */
+  public BitmapDrawable tileObject;
+
   /* ID incremented every time the clipping rectangles of any GC
      changes.  */
   private static long clip_serial;
@@ -86,6 +98,7 @@ public final class EmacsGC extends EmacsHandleObject
   markDirty (boolean clipRectsChanged)
   {
     int i;
+    Bitmap stippleBitmap;
 
     if (clipRectsChanged)
       {
@@ -110,12 +123,85 @@ public final class EmacsGC extends EmacsHandleObject
     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
+       modifying such objects.  */
+
+    if (stipple != null)
+      {
+       stippleBitmap = stipple.getBitmap ();
+
+       /* Allocate a new tile object if none is already present or it
+          cannot be reconfigured.  */
+       if ((tileObject == null)
+           || (Build.VERSION.SDK_INT < Build.VERSION_CODES.S))
+         {
+           tileObject = new BitmapDrawable (EmacsService.resources,
+                                            stippleBitmap);
+           tileObject.setTileModeXY (TileMode.MIRROR, TileMode.MIRROR);
+         }
+       else
+         /* Otherwise, update the existing tile object with the new
+            bitmap.  */
+         tileObject.setBitmap (stippleBitmap);
+      }
+    else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+            && tileObject != null)
+      tileObject.setBitmap (null);
+    else if (tileObject != null)
+      tileObject = null;
   }
 
-  public void
-  resetXfermode ()
+  /* Prepare the tile object to draw a stippled image onto a section of
+     a drawable defined by RECT.  It is an error to call this function
+     unless the `stipple' field of the GContext is set.  */
+
+  private void
+  prepareStipple (Rect rect)
   {
-    gcPaint.setXfermode (function == GC_XOR
-                        ? xorAlu : srcInAlu);
+    int sx, sy; /* Stipple origin.  */
+    int bw, bh; /* Stipple size.  */
+    Bitmap bitmap;
+    Rect boundsRect;
+
+    /* Retrieve the dimensions of the stipple bitmap, which doubles as
+       the unit of advance for this stipple.  */
+    bitmap = tileObject.getBitmap ();
+    bw     = bitmap.getWidth ();
+    bh     = bitmap.getHeight ();
+
+    /* Align the lower left corner of the bounds rectangle to the
+       initial position of the stipple.  */
+    sx = (rect.left % bw) * -1 + (-ts_origin_x % bw) * -1;
+    sy = (rect.top  % bh) * -1 + (-ts_origin_y % bh) * -1;
+    boundsRect = new Rect (rect.left + sx, rect.top + sy,
+                          rect.right, rect.bottom);
+    tileObject.setBounds (boundsRect);
+  }
+
+  /* Fill the rectangle BOUNDS in the provided CANVAS with the stipple
+     pattern defined for this GContext, in the foreground color where
+     the pattern is on, and in the background color where off.  */
+
+  protected void
+  blitOpaqueStipple (Canvas canvas, Rect rect)
+  {
+    ColorFilter filter;
+
+    prepareStipple (rect);
+    filter = new PorterDuffColorFilter (foreground | 0xff000000,
+                                       Mode.SRC_IN);
+    tileObject.setColorFilter (filter);
+
+    canvas.save ();
+    canvas.clipRect (rect);
+
+    tileObject.draw (canvas);
+    filter = new PorterDuffColorFilter (background | 0xff000000,
+                                       Mode.SRC_OUT);
+    tileObject.setColorFilter (filter);
+    tileObject.draw (canvas);
+    canvas.restore ();
   }
 };
index 2d4079c11b052a84b947d786e403b48ed9a85823..8e459ce4cdc4b99eb48b0b6a86d1d9f4e0d76811 100644 (file)
@@ -64,6 +64,7 @@ import android.content.pm.PackageManager;
 
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 
 import android.hardware.input.InputManager;
 
@@ -146,6 +147,9 @@ public final class EmacsService extends Service
      thread.  */
   private Thread mainThread;
 
+  /* "Resources" object required by GContext bookkeeping.  */
+  public static Resources resources;
+
   static
   {
     servicingQuery = new AtomicInteger ();
@@ -238,10 +242,11 @@ public final class EmacsService extends Service
     super.onCreate ();
 
     SERVICE = this;
+    resources = getResources ();
     handler = new Handler (Looper.getMainLooper ());
     manager = getAssets ();
     app_context = getApplicationContext ();
-    metrics = getResources ().getDisplayMetrics ();
+    metrics = resources.getDisplayMetrics ();
     pixelDensityX = metrics.xdpi;
     pixelDensityY = metrics.ydpi;
     tempScaledDensity = ((getScaledDensity (metrics)
index 7a7eadc946afca3c763262a9125e287d3c3f9fce..e44b58c5973bfb80954c83aafdaa8dadaef33c72 100644 (file)
@@ -4882,8 +4882,8 @@ android_pixmap
 android_create_bitmap_from_data (char *bits, unsigned int width,
                                 unsigned int height)
 {
-  return android_create_pixmap_from_bitmap_data (bits, 1, 0,
-                                                width, height, 1);
+  return android_create_pixmap_from_bitmap_data (bits, width, height,
+                                                1, 0, 1);
 }
 
 struct android_image *
@@ -5994,40 +5994,22 @@ android_toggle_on_screen_keyboard (android_window window, bool show)
 
 \f
 
-#if defined __clang_major__ && __clang_major__ < 5
-# define HAS_BUILTIN_TRAP 0
-#elif 3 < __GNUC__ + (3 < __GNUC_MINOR__ + (4 <= __GNUC_PATCHLEVEL__))
-# define HAS_BUILTIN_TRAP 1
-#elif defined __has_builtin
-# define HAS_BUILTIN_TRAP __has_builtin (__builtin_trap)
-#else /* !__has_builtin */
-# define HAS_BUILTIN_TRAP 0
-#endif /* defined __clang_major__ && __clang_major__ < 5 */
-
 /* emacs_abort implementation for Android.  This logs a stack
    trace.  */
 
 void
 emacs_abort (void)
 {
-#ifndef HAS_BUILTIN_TRAP
   volatile char *foo;
-#endif /* !HAS_BUILTIN_TRAP */
 
   __android_log_print (ANDROID_LOG_FATAL, __func__,
                       "emacs_abort called, please review the following"
                       " stack trace");
 
-#ifndef HAS_BUILTIN_TRAP
   /* Induce a NULL pointer dereference to make debuggerd generate a
      tombstone.  */
   foo = NULL;
   *foo = '\0';
-#else /* HAS_BUILTIN_TRAP */
-  /* Crash through __builtin_trap instead.  This appears to more
-     uniformly elicit crash reports from debuggerd.  */
-  __builtin_trap ();
-#endif /* !HAS_BUILTIN_TRAP */
 
   abort ();
 }
index f29377f359678e3646174b8c2d8a67d6c19895be..c3c2d61082b20d12a4d6e3df9b2efdb497ffb412 100644 (file)
@@ -69,12 +69,6 @@ typedef struct
   unsigned width, height;
 } Emacs_Rectangle;
 
-#else
-
-typedef struct android_rectangle Emacs_Rectangle;
-
-#endif
-
 /* XGCValues-like struct used by non-X GUI code.  */
 typedef struct
 {
@@ -88,6 +82,19 @@ typedef struct
 #define GCForeground 0x01
 #define GCBackground 0x02
 
+#else
+
+typedef struct android_rectangle Emacs_Rectangle;
+typedef struct android_gc_values Emacs_GC;
+
+#define GCForeground           ANDROID_GC_FOREGROUND
+#define GCBackground           ANDROID_GC_BACKGROUND
+#define GCFillStyle            ANDROID_GC_FILL_STYLE
+#define GCStipple              ANDROID_GC_STIPPLE
+#define FillOpaqueStippled     ANDROID_FILL_OPAQUE_STIPPLED
+
+#endif
+
 #endif /* HAVE_X_WINDOWS */
 
 #ifdef MSDOS
index 275d13985aada36243645c5eeaa0372be57da9b6..8415ce51586bc3f3e712d8916a7021ea1080420a 100644 (file)
@@ -95,7 +95,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
  # define  PATH_DOC                    "/assets/etc/"
  # define  PATH_INFO                   "/assets/info/"
  # define  PATH_GAME                   ""
- # define  PATH_BITMAPS                        ""
+ # define  PATH_BITMAPS                        "/assets/bitmaps/"
 
 extern char *android_site_load_path;
 extern char *android_lib_dir;
index 3968145728fd98966e825ee667182754c645cf20..3028c2e707a47abb3aac7bb8a586224a788f4348 100644 (file)
@@ -419,7 +419,7 @@ x_bitmap_stipple (struct frame *f, Pixmap pixmap)
 #endif /* USE_CAIRO */
 #endif
 
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_ANDROID)
 ptrdiff_t
 image_bitmap_pixmap (struct frame *f, ptrdiff_t id)
 {
index 1ed394b94585e731bf7d2c25346221e76a0e3b1a..b90ca857dd40808b1f2ef827626349a12eb3332c 100644 (file)
@@ -503,6 +503,10 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
 
   if (with_background)
     {
+      /* The background should have been filled in advance if a stipple
+        is enabled.  */
+      eassert (s->gc->fill_style != ANDROID_FILL_OPAQUE_STIPPLED);
+
       /* Fill the background.  First, offset the background rectangle
         to become relative from text_rectangle.x,
         text_rectangle.y.  */
index d4583e1a78f8aa4b4e129fe767e7aa962ea69446..d307dbaa246f289ee831f6a122a800235c7a5b54 100644 (file)
@@ -619,21 +619,7 @@ static struct android_gc *
 x_create_gc (struct frame *f, unsigned long value_mask,
             Emacs_GC *xgcv)
 {
-  struct android_gc_values gcv;
-  unsigned long mask;
-
-  gcv.foreground = xgcv->foreground;
-  gcv.background = xgcv->background;
-
-  mask = 0;
-
-  if (value_mask & GCForeground)
-    mask |= ANDROID_GC_FOREGROUND;
-
-  if (value_mask & GCBackground)
-    mask |= ANDROID_GC_BACKGROUND;
-
-  return android_create_gc (mask, &gcv);
+  return android_create_gc (value_mask, xgcv);
 }
 
 static void
@@ -4630,14 +4616,18 @@ prepare_face_for_display (struct frame *f, struct face *face)
 #endif
 
       block_input ();
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
       if (face->stipple)
        {
          egc.fill_style = FillOpaqueStippled;
+#ifndef ANDROID_STUBIFY
          egc.stipple = image_bitmap_pixmap (f, face->stipple);
+#else /* !ANDROID_STUBIFY */
+         emacs_abort ();
+#endif /* !ANDROID_STUBIFY */
          mask |= GCFillStyle | GCStipple;
        }
-#endif
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
       face->gc = x_create_gc (f, mask, &egc);
       if (face->font)
        font_prepare_for_face (f, face);