Files
emacs/java/org/gnu/emacs/EmacsSdk7FontDriver.java
Po Lu f9732131cf Update Android port
* configure.ac (ANDROID_MIN_SDK): New variable.
(DX): Remove and replace with D8.
(XCONFIGURE): Check for the minimum version of Android the cross
compiler compiles for.  Generate java/AndroidManifest.xml from
java/AndroidManifest.xml.in.  Allow using Zlib on Android.

* java/AndroidManifest.xml.in: New file.  Use the minimum SDK
detected by configure.

* java/Makefile.in (top_srcdir, version): New variables.
(DX, D8): Replace with D8.
(ANDROID_MIN_SDK, APK_NAME): New variables.
(.PHONY):
(.PRECIOUS):
(classes.dex):
(emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'.

* java/debug.sh: New option --attach-existing.  Attach to an
existing Emacs instance when specified.

* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
field `isPaused'.
(invalidateFocus1): Fix infinite recursion.
(detachWindow): Deiconify window.
(attachWindow): Iconify the window if the activity is paused.
(onCreate): Use the ``no title bar'' theme.
(onPause, onResume): New functions.
* java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown)
(sendTouchMove, sendWheel, sendIconified, sendDeiconified): New
functions.
* java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface):
(list): Remove logging for code that is mostly going to be unused.
* java/org/gnu/emacs/EmacsService.java (ringBell, queryTree)
(getScreenWidth, getScreenHeight, detectMouse): New functions.
* java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
(surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra
debug logging.  Avoid deadlock in surfaceCreated.

* java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard
to make the SurfaceView respect Z order.  It didn't work.
(handleDirtyBitmap): Copy over the contents from the old bitmap.
(explicitlyDirtyBitmap): New function.
(onLayout): Don't dirty bitmap if unnecessary.
(damageRect, swapBuffers): Don't synchronize so hard.
(onTouchEvent): Call window.onTouchEvent instead.
(moveChildToBack, raise, lower): New functions.

* java/org/gnu/emacs/EmacsWindow.java (Coordinate): New
subclass.
(pointerMap, isMapped, isIconified, dontFocusOnMap)
(dontAcceptFocus): New fields.
(EmacsWindow): Don't immediately register unmapped window.
(viewLayout): Send configure event outside the lock.
(requestViewLayout): Explicitly dirty the bitmap.
(mapWindow): Register the window now.  Respect dontFocusOnMap.
(unmapWindow): Unregister the window now.
(figureChange, onTouchEvent): New functions.
(onSomeKindOfMotionEvent): Handle scroll wheel events.
(reparentTo, makeInputFocus, raise, lower, getWindowGeometry)
(noticeIconified, noticeDeiconified, setDontAcceptFocus)
(setDontFocusOnMap, getDontFocusOnMap): New functions.
* java/org/gnu/emacs/EmacsWindowAttachmentManager.java
(registerWindow, detachWindow): Synchronize.
(noticeIconified, noticeDeiconified): New functions.
(copyWindows): New function.

* lisp/frame.el (frame-geometry, frame-edges)
(mouse-absolute-pixel-position, set-mouse-absolute-pixel-position)
(frame-list-z-order, frame-restack, display-mouse-p)
(display-monitor-attributes-list): Implement on Android.

* lisp/mwheel.el (mouse-wheel-down-event):
(mouse-wheel-up-event):
(mouse-wheel-left-event):
(mouse-wheel-right-event): Define on Android.

* src/android.c (struct android_emacs_service): New methods
`ringBell', `queryTree', `getScreenWidth', `getScreenHeight',
and `detectMouse'.
(struct android_event_queue, android_init_events)
(android_next_event, android_write_event): Remove write limit.
(android_file_access_p): Handle directories correcty.
(android_close): Fix coding style.
(android_fclose): New function.
(android_init_emacs_service): Initialize new methods.
(android_reparent_window): Implement function.
(android_bell, android_set_input_focus, android_raise_window)
(android_lower_window, android_query_tree, android_get_geometry)
(android_get_screen_width, android_get_screen_height)
(android_get_mm_width, android_get_mm_height, android_detect_mouse)
(android_set_dont_focus_on_map, android_set_dont_accept_focus):
New functions.
(struct android_dir): New structure.
(android_opendir, android_readdir, android_closedir): New
functions.
(emacs_abort): Implement here on Android and poke debuggerd into
generating a tombstone.

* src/android.h: Update prototypes.

* src/androidfns.c (android_set_parent_frame): New function.
(android_default_font_parameter): Use sane font size by default.
(Fx_display_pixel_width, Fx_display_pixel_height)
(Fx_display_mm_width, Fx_display_mm_height)
(Fx_display_monitor_attributes_list): Rename to start with
`android-'.  Implement.  Fiddle with documentation to introduce
Android specific nuances.
(Fandroid_display_monitor_attributes_list): New function.
(Fx_frame_geometry, frame_geometry): New function.
(Fandroid_frame_geometry): Implement correctly.
(Fx_frame_list_z_order): Rename to start with `android-'.
(android_frame_list_z_order, Fandroid_frame_list_z_order):
Implement.
(Fx_frame_restack): Rename to start with `android-'.
(Fandroid_frame_restack): ``Implement''.
(Fx_mouse_absolute_pixel_position): Rename to start with
`android-'.
(Fandroid_mouse_absolute_pixel_position): ``Implement''.
(Fx_set_mouse_absolute_pixel_position): Rename to start with
`android-'.
(Fandroid_set_mouse_absolute_pixel_position): ``Implement''.
(Fandroid_detect_mouse): New function.
(android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when
clearing area.
(android_set_no_focus_on_map, android_set_no_accept_focus): New
functions.
(android_frame_parm_handlers): Register new frame parameter
handlers.
(syms_of_androidfns): Update appropriately.

* src/androidfont.c (androidfont_draw): Use
FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.

* src/androidgui.h (enum android_event_type): New events.
(struct android_touch_event, struct android_wheel_event)
(struct android_iconify_event): New structures.
(union android_event): Add new events.

* src/androidterm.c (android_clear_frame): Use
FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
(android_flash, android_ring_bell): Implement bell ringing.
(android_toggle_invisible_pointer): Don't TODO function that
can't be implemented.
(show_back_buffer, android_flush_dirty_back_buffer_on): Check if
a buffer flip is required before doing the flip.
(android_lower_frame, android_raise_frame): Implement functions.
(android_update_tools, android_find_tool): New functions.
(handle_one_android_event): Handle new iconification, wheel and
touch events.
(android_read_socket): Implement pending-autoraise-frames.
(android_frame_up_to_date): Implement bell ringing.
(android_buffer_flipping_unblocked_hook): Check if a buffer flip
is required before doing the flip.
(android_focus_frame, android_frame_highlight)
(android_frame_unhighlight): New function.
(android_frame_rehighlight): Implement functions.
(android_iconify_frame): Always display error.
(android_set_alpha): Update commentary.
(android_free_frame_resources): Free frame touch points.
(android_scroll_run, android_flip_and_flush)
(android_clear_rectangle, android_draw_fringe_bitmap)
(android_draw_glyph_string_background, android_fill_triangle)
(android_clear_point, android_draw_relief_rect)
(android_draw_box_rect, android_draw_glyph_string_bg_rect)
(android_draw_image_foreground, android_draw_stretch_glyph_string)
(android_draw_underwave, android_draw_glyph_string_foreground)
(android_draw_composite_glyph_string_foreground)
(android_draw_glyphless_glyph_string_foreground)
(android_draw_glyph_string, android_clear_frame_area)
(android_clear_under_internal_border, android_draw_hollow_cursor)
(android_draw_bar_cursor, android_draw_vertical_window_border)
(android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE
instead of FRAME_ANDROID_WINDOW for drawing operations.

* src/androidterm.h (struct android_touch_point): New structure.
(struct android_output): New fields.
(FRAME_ANDROID_NEED_BUFFER_FLIP): New macro.

* src/dired.c (emacs_readdir, open_directory)
(directory_files_internal_unwind, read_dirent)
(directory_files_internal, file_name_completion): Add
indirection over readdir and opendir.  Use android variants on
Android.

* src/dispnew.c (Fopen_termscript):
* src/fileio.c (fclose_unwind): Use emacs_fclose.
(Faccess_file): Call android_file_access_p.
(file_accessible_directory_p): Append right suffix to Android
assets directory.
(do_auto_save_unwind): Use emacs_fclose.
* src/keyboard.c (lispy_function_keys): Use right function key
for page up and page down.
(Fopen_dribble_file): Use emacs_fclose.

* src/lisp.h: New prototype emacs_fclose.

* src/lread.c (close_infile_unwind): Use emacs_fclose.

* src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation.
(sfnt_prepare_raster): Compute raster width and height
consistently with outline building.
(sfnt_build_outline_edges): Use the same offsets used to set
offy and offx.
(main): Adjust debug code.

* src/sfntfont-android.c (sfntfont_android_saturate32): Delete
function.
(sfntfont_android_blend, sfntfont_android_blendrgb): Remove
unnecessary debug code.
(sfntfont_android_composite_bitmap): Prevent out of bounds
write.
(sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE.
(init_sfntfont_android): Initialize Monospace Serif font to
something sensible.
* src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics
before summing up pcm.
(sfntfont_draw): Use s->font instead of s->face->font.

* src/sysdep.c (emacs_fclose): Wrap around android_fclose on
android.

* src/term.c (Fsuspend_tty):
(delete_tty): Use emacs_fclose.
* src/verbose.mk.in (AM_V_DX): Replace with D8 version.
2023-01-13 15:53:08 +08:00

551 lines
14 KiB
Java

/* Font backend 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 <https://www.gnu.org/licenses/>. */
package org.gnu.emacs;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Canvas;
import android.util.Log;
import android.os.Build;
public class EmacsSdk7FontDriver extends EmacsFontDriver
{
private static final String TOFU_STRING = "\uDB3F\uDFFD";
private static final String EM_STRING = "m";
private static final String TAG = "EmacsSdk7FontDriver";
protected class Sdk7Typeface
{
/* The typeface and paint. */
public Typeface typeface;
public Paint typefacePaint;
public String familyName;
public int slant, width, weight, spacing;
public
Sdk7Typeface (String fileName, Typeface typeface)
{
String style, testString;
int index, measured, i;
float[] widths;
slant = NORMAL;
weight = REGULAR;
width = UNSPECIFIED;
spacing = PROPORTIONAL;
this.typeface = typeface;
typefacePaint = new Paint ();
typefacePaint.setAntiAlias (true);
typefacePaint.setTypeface (typeface);
/* For the calls to measureText below. */
typefacePaint.setTextSize (10.0f);
/* Parse the file name into some useful data. First, strip off
the extension. */
fileName = fileName.split ("\\.", 2)[0];
/* Next, split the file name by dashes. Everything before the
last dash is part of the family name. */
index = fileName.lastIndexOf ("-");
if (index > 0)
{
style = fileName.substring (index + 1, fileName.length ());
familyName = fileName.substring (0, index);
/* Look for something describing the weight. */
if (style.contains ("Thin"))
weight = THIN;
else if (style.contains ("UltraLight"))
weight = ULTRA_LIGHT;
else if (style.contains ("SemiLight"))
weight = SEMI_LIGHT;
else if (style.contains ("Light"))
weight = LIGHT;
else if (style.contains ("Medium"))
weight = MEDIUM;
else if (style.contains ("SemiBold"))
weight = SEMI_BOLD;
else if (style.contains ("ExtraBold"))
weight = EXTRA_BOLD;
else if (style.contains ("Bold"))
weight = BOLD;
else if (style.contains ("Black"))
weight = BLACK;
else if (style.contains ("UltraHeavy"))
weight = ULTRA_HEAVY;
/* And the slant. */
if (style.contains ("ReverseOblique"))
slant = OBLIQUE;
else if (style.contains ("ReverseItalic"))
slant = REVERSE_ITALIC;
else if (style.contains ("Italic"))
slant = ITALIC;
else if (style.contains ("Oblique"))
slant = OBLIQUE;
/* Finally, the width. */
if (style.contains ("UltraCondensed"))
width = ULTRA_CONDENSED;
else if (style.contains ("ExtraCondensed"))
width = EXTRA_CONDENSED;
else if (style.contains ("SemiCondensed"))
width = SEMI_CONDENSED;
else if (style.contains ("Condensed"))
width = CONDENSED;
else if (style.contains ("SemiExpanded"))
width = SEMI_EXPANDED;
else if (style.contains ("ExtraExpanded"))
width = EXTRA_EXPANDED;
else if (style.contains ("UltraExpanded"))
width = ULTRA_EXPANDED;
else if (style.contains ("Expanded"))
width = EXPANDED;
/* Guess the spacing information. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
widths = new float[testString.length ()];
measured = typefacePaint.getTextWidths (testString,
0, testString.length (),
widths);
spacing = MONO;
for (i = 0; i < measured; ++i)
{
if (i != 0 && widths[i - 1] != widths[i])
/* This isn't a monospace font. */
spacing = PROPORTIONAL;
}
}
else
familyName = fileName;
}
@Override
public String
toString ()
{
return ("Sdk7Typeface ("
+ String.valueOf (familyName) + ", "
+ String.valueOf (slant) + ", "
+ String.valueOf (width) + ", "
+ String.valueOf (weight) + ", "
+ String.valueOf (spacing) + ")");
}
};
protected class Sdk7FontEntity extends FontEntity
{
/* The typeface. */
public Sdk7Typeface typeface;
public
Sdk7FontEntity (Sdk7Typeface typeface)
{
float width;
foundry = "Google";
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
this.typeface = typeface;
}
};
protected class Sdk7FontObject extends FontObject
{
/* The typeface. */
public Sdk7Typeface typeface;
public
Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
{
float totalWidth;
String testWidth, testString;
this.typeface = typeface;
this.pixelSize = pixelSize;
family = typeface.familyName;
adstyle = null;
weight = typeface.weight;
slant = typeface.slant;
spacing = typeface.spacing;
width = typeface.width;
dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
/* Compute the ascent and descent. */
typeface.typefacePaint.setTextSize (pixelSize);
ascent
= Math.round (-typeface.typefacePaint.ascent ());
descent
= Math.round (typeface.typefacePaint.descent ());
/* Compute the average width. */
testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
totalWidth = typeface.typefacePaint.measureText (testString);
if (totalWidth > 0)
avgwidth = Math.round (totalWidth
/ testString.length ());
/* Android doesn't expose the font average width and height
information, so this will have to do. */
minWidth = maxWidth = avgwidth;
/* This is different from avgwidth in the font spec! */
averageWidth = avgwidth;
/* Set the space width. */
totalWidth = typeface.typefacePaint.measureText (" ");
spaceWidth = Math.round (totalWidth);
/* Set the height and default ascent. */
height = ascent + descent;
defaultAscent = ascent;
}
};
private String[] fontFamilyList;
private Sdk7Typeface[] typefaceList;
private Sdk7Typeface fallbackTypeface;
public
EmacsSdk7FontDriver ()
{
int i;
File systemFontsDirectory, fontFile;
Typeface typeface;
systemFontsDirectory = new File ("/system/fonts");
fontFamilyList = systemFontsDirectory.list ();
typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
/* It would be nice to avoid opening each and every font upon
startup. But that doesn't seem to be possible on
Android. */
for (i = 0; i < fontFamilyList.length; ++i)
{
fontFile = new File (systemFontsDirectory,
fontFamilyList[i]);
typeface = Typeface.createFromFile (fontFile);
typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
typeface);
}
/* Initialize the default monospace and serif typefaces. */
fallbackTypeface = new Sdk7Typeface ("monospace",
Typeface.MONOSPACE);
typefaceList[fontFamilyList.length] = fallbackTypeface;
fallbackTypeface = new Sdk7Typeface ("Monospace",
Typeface.MONOSPACE);
typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
fallbackTypeface = new Sdk7Typeface ("Sans Serif",
Typeface.DEFAULT);
typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
}
private boolean
checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
{
if (fontSpec.family != null
&& !fontSpec.family.equals (typeface.familyName))
return false;
if (fontSpec.slant != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.spacing != null
&& !fontSpec.spacing.equals (typeface.spacing))
return false;
if (fontSpec.weight != null
&& !fontSpec.weight.equals (typeface.weight))
return false;
if (fontSpec.width != null
&& !fontSpec.width.equals (typeface.width))
return false;
return true;
}
@Override
public FontEntity[]
list (FontSpec fontSpec)
{
LinkedList<FontEntity> list;
int i;
list = new LinkedList<FontEntity> ();
for (i = 0; i < typefaceList.length; ++i)
{
if (checkMatch (typefaceList[i], fontSpec))
list.add (new Sdk7FontEntity (typefaceList[i]));
}
return (FontEntity[]) list.toArray (new FontEntity[0]);
}
@Override
public FontEntity
match (FontSpec fontSpec)
{
FontEntity[] entities;
int i;
entities = this.list (fontSpec);
if (entities.length == 0)
return new Sdk7FontEntity (fallbackTypeface);
return entities[0];
}
@Override
public String[]
listFamilies ()
{
return fontFamilyList;
}
@Override
public FontObject
openFont (FontEntity fontEntity, int pixelSize)
{
return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
pixelSize);
}
@Override
public int
hasChar (FontSpec font, char charCode)
{
float missingGlyphWidth, emGlyphWidth, width;
Rect rect1, rect2;
Paint paint;
Sdk7FontObject fontObject;
if (font instanceof Sdk7FontObject)
{
fontObject = (Sdk7FontObject) font;
paint = fontObject.typeface.typefacePaint;
}
else
paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
paint.setTextSize (10);
if (Character.isWhitespace (charCode))
return 1;
missingGlyphWidth = paint.measureText (TOFU_STRING);
emGlyphWidth = paint.measureText (EM_STRING);
width = paint.measureText ("" + charCode);
if (width == 0f)
return 0;
if (width != missingGlyphWidth)
return 1;
rect1 = new Rect ();
rect2 = new Rect ();
paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
rect1);
paint.getTextBounds ("" + charCode, 0, 1, rect2);
return rect1.equals (rect2) ? 0 : 1;
}
private void
textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
Paint paint, Rect bounds)
{
char[] text;
text = new char[1];
text[0] = (char) code;
paint.getTextBounds (text, 0, 1, bounds);
/* bounds is the bounding box of the glyph corresponding to CODE.
Translate these into XCharStruct values.
The origin is at 0, 0, and lbearing is the distance counting
rightwards from the origin to the left most pixel in the glyph
raster. rbearing is the distance between the origin and the
rightmost pixel in the glyph raster. ascent is the distance
counting upwards between the the topmost pixel in the glyph
raster. descent is the distance (once again counting
downwards) between the origin and the bottommost pixel in the
glyph raster.
width is the distance between the origin and the origin of any
character to the right. */
metrics.lbearing = (short) bounds.left;
metrics.rbearing = (short) bounds.right;
metrics.ascent = (short) -bounds.top;
metrics.descent = (short) bounds.bottom;
metrics.width = (short) paint.measureText ("" + text[0]);
}
@Override
public void
textExtents (FontObject font, int code[], FontMetrics fontMetrics)
{
int i;
Paint paintCache;
Rect boundsCache;
Sdk7FontObject fontObject;
char[] text;
float width;
fontObject = (Sdk7FontObject) font;
paintCache = fontObject.typeface.typefacePaint;
paintCache.setTextSize (fontObject.pixelSize);
boundsCache = new Rect ();
if (code.length == 0)
{
fontMetrics.lbearing = 0;
fontMetrics.rbearing = 0;
fontMetrics.ascent = 0;
fontMetrics.descent = 0;
fontMetrics.width = 0;
}
else if (code.length == 1)
textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
paintCache, boundsCache);
else
{
text = new char[code.length];
for (i = 0; i < code.length; ++i)
text[i] = (char) code[i];
paintCache.getTextBounds (text, 0, code.length,
boundsCache);
width = paintCache.measureText (text, 0, code.length);
fontMetrics.lbearing = (short) boundsCache.left;
fontMetrics.rbearing = (short) boundsCache.right;
fontMetrics.ascent = (short) -boundsCache.top;
fontMetrics.descent = (short) boundsCache.bottom;
fontMetrics.width = (short) Math.round (width);
}
}
@Override
public int
encodeChar (FontObject fontObject, char charCode)
{
return charCode;
}
@Override
public int
draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
int[] chars, int x, int y, int backgroundWidth,
boolean withBackground)
{
Rect backgroundRect, bounds;
Sdk7FontObject sdk7FontObject;
char[] charsArray;
int i;
Canvas canvas;
Paint paint;
sdk7FontObject = (Sdk7FontObject) fontObject;
charsArray = new char[chars.length];
for (i = 0; i < chars.length; ++i)
charsArray[i] = (char) chars[i];
backgroundRect = new Rect ();
backgroundRect.top = y - sdk7FontObject.ascent;
backgroundRect.left = x;
backgroundRect.right = x + backgroundWidth;
backgroundRect.bottom = y + sdk7FontObject.descent;
canvas = drawable.lockCanvas ();
if (canvas == null)
return 0;
canvas.save ();
paint = gc.gcPaint;
if (gc.real_clip_rects != null)
{
for (i = 0; i < gc.real_clip_rects.length; ++i)
canvas.clipRect (gc.real_clip_rects[i]);
}
paint.setStyle (Paint.Style.FILL);
if (withBackground)
{
paint.setColor (gc.background | 0xff000000);
canvas.drawRect (backgroundRect, paint);
paint.setColor (gc.foreground | 0xff000000);
}
paint.setTextSize (sdk7FontObject.pixelSize);
paint.setTypeface (sdk7FontObject.typeface.typeface);
paint.setAntiAlias (true);
canvas.drawText (charsArray, 0, chars.length, x, y, paint);
canvas.restore ();
bounds = new Rect ();
paint.getTextBounds (charsArray, 0, chars.length, bounds);
bounds.offset (x, y);
bounds.union (backgroundRect);
drawable.damageRect (bounds);
paint.setAntiAlias (false);
return 1;
}
};