From e658a6938e3b7a8a7c0be8b74fbd885787c26df6 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 28 Apr 2024 16:58:58 +0800 Subject: Implement dots and dashes on Android * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine) (measureLine, polyDashPattern): New function. (perform): Delegate to polyDashPattern if the line style is not LineSolid. Also simplify now that anti-aliasing need no longer be taken into account. * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Mention omission in commentary. * java/org/gnu/emacs/EmacsGC.java (EmacsGC): Disable anti-aliasing in default paint object. : New fields. (markDirty): Apply stroke width. * src/android.c (android_init_emacs_gc_class): Initialize new fields. (android_create_gc, android_free_gc, android_change_gc) (android_set_dashes, android_get_gc_values): * src/androidgui.h (enum android_line_style) (enum android_gc_value_mask, struct android_gc): Introduce line style, width, dash offset and dash GC attributes. * src/androidterm.c (android_draw_dash, android_fill_underline) (android_draw_glyph_string): Port from X. * src/xterm.c (x_draw_dash): Delete redundant code. --- java/org/gnu/emacs/EmacsDrawLine.java | 109 ++++++++++++++++++++++++++--- java/org/gnu/emacs/EmacsDrawRectangle.java | 3 + java/org/gnu/emacs/EmacsGC.java | 12 +++- 3 files changed, 113 insertions(+), 11 deletions(-) (limited to 'java/org/gnu') diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 61b7d54d63c..a49fe96c26e 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -25,6 +25,97 @@ import android.graphics.Rect; public final class EmacsDrawLine { + /* Return the normalized slope and magnitude of a line whose extrema + are DX and DY removed, on the X and Y axes respectively, from its + origin point. */ + + private static float[] + measureLine (float dx, float dy) + { + float hypot; + + if (dx == 0f && dy == 0f) + return new float[] { 0f, 0f, 0f, }; + + if (dx == 0f) + return new float[] { 0f, dy > 0f ? 1f : -1f, Math.abs (dy), }; + else if (dy == 0f) + return new float[] { dx > 0f ? 1f : -1f, 0f, Math.abs (dx), }; + else + { + hypot = (float) Math.hypot (dx, dy); + return new float[] { dx / hypot, dy / hypot, hypot, }; + } + } + + private static void + polyDashPattern (EmacsGC gc, Canvas canvas, Paint paint, float x0, + float y0, float x1, float y1) + { + int patternTotal, i, offset; + float dx, dy, mag, dash_mag, rem, lx1, ly1; + float[] measured; + boolean which; + + /* Compute the total length of this pattern. */ + patternTotal = 0; + for (i = 0; i < gc.dashes.length; ++i) + patternTotal += gc.dashes[i]; + if ((gc.dashes.length & 1) != 0) + patternTotal += patternTotal; + + /* Subtract as much of the offset as does not contribute to the + phase at the first pixel of the line. */ + offset = gc.dash_offset % patternTotal; + + /* Set I to the first dash that ought to be drawn and WHICH to its + phase. */ + i = 0; + which = true; + while (offset >= gc.dashes[i]) + { + offset -= gc.dashes[i++]; + if (i >= gc.dashes.length) + i = 0; + which = !which; + } + + /* Compute the length of the first visible segment. */ + dash_mag = gc.dashes[i] - offset; + + /* Compute the slope of the line. */ + dx = x1 - x0; + dy = y1 - y0; + measured = measureLine (dx, dy); + dx = measured[0]; + dy = measured[1]; + rem = mag = measured[2]; + lx1 = x0; + ly1 = y0; + + while (rem > 0f) + { + dash_mag = Math.min (dash_mag, rem); + rem -= dash_mag; + + /* End of this segment. */ + x1 = (mag - rem) * dx + x0; + y1 = (mag - rem) * dy + y0; + + if (which) + canvas.drawLine (lx1, ly1, x1, y1, paint); + which = !which; + + /* Start of the next segment. */ + lx1 = x1; + ly1 = y1; + i++; + if (i >= gc.dashes.length) + i = 0; + dash_mag = gc.dashes[i]; + } + } + public static void perform (EmacsDrawable drawable, EmacsGC gc, int x, int y, int x2, int y2) @@ -52,22 +143,20 @@ public final class EmacsDrawLine if (canvas == null) return; - paint.setStyle (Paint.Style.FILL); - /* Since drawLine has PostScript style behavior, adjust the coordinates appropriately. - The left most pixel of a straight line is always partially - filled. Patch it in manually. */ + The leftmost pixel of a straight line is always partially filled. + Patch it in manually. */ if (gc.clip_mask == null) { - canvas.drawLine ((float) x + 0.5f, (float) y + 0.5f, - (float) x2 + 0.5f, (float) y2 + 0.5f, - paint); - - if (x2 > x) - canvas.drawRect (new Rect (x, y, x + 1, y + 1), paint); + if (gc.line_style != EmacsGC.GC_LINE_ON_OFF_DASH) + canvas.drawLine ((float) x, (float) y, (float) x2, (float) y2, + paint); + else + polyDashPattern (gc, canvas, paint, (float) x, (float) y, + (float) x2, (float) y2); } /* DrawLine with clip mask not implemented; it is not used by diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index a8f68c6530a..e40a7c16068 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -52,6 +52,9 @@ public final class EmacsDrawRectangle paint = gc.gcPaint; paint.setStyle (Paint.Style.STROKE); + /* This graphics request, in contrast to X, does not presently + respect the GC's line style. */ + if (gc.clip_mask == null) /* Use canvas.drawRect with a RectF. That seems to reliably get PostScript behavior. */ diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java index b2474c5bd76..ec2b9c9e475 100644 --- a/java/org/gnu/emacs/EmacsGC.java +++ b/java/org/gnu/emacs/EmacsGC.java @@ -46,12 +46,17 @@ public final class EmacsGC extends EmacsHandleObject public static final int GC_FILL_SOLID = 0; public static final int GC_FILL_OPAQUE_STIPPLED = 1; + 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; public int ts_origin_x, ts_origin_y; + public int line_style, line_width; + public int dashes[], dash_offset; public Rect clip_rects[], real_clip_rects[]; public EmacsPixmap clip_mask, stipple; public Paint gcPaint; @@ -89,6 +94,10 @@ public final class EmacsGC extends EmacsHandleObject foreground = 0; background = 0xffffff; gcPaint = new Paint (); + + /* Android S and above enable anti-aliasing unless explicitly told + otherwise. */ + gcPaint.setAntiAlias (false); } /* Mark this GC as dirty. Apply parameters to the paint and @@ -119,7 +128,8 @@ public final class EmacsGC extends EmacsHandleObject clipRectID = ++clip_serial; } - gcPaint.setStrokeWidth (1f); + /* 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); -- cgit v1.2.1