emacs/src/androidfont.c

1103 lines
30 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Android fallback font driver.
Copyright (C) 2023-2024 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/>. */
/* Due to the terrible nature of the Android Typeface subsystems, this
font driver is only used as a fallback when sfntfont-android.c
fails to enumerate any fonts at all. */
#include <config.h>
#include "lisp.h"
#include "dispextern.h"
#include "composite.h"
#include "blockinput.h"
#include "charset.h"
#include "frame.h"
#include "window.h"
#include "fontset.h"
#include "androidterm.h"
#include "character.h"
#include "coding.h"
#include "font.h"
#include "termchar.h"
#include "pdumper.h"
#include "android.h"
#ifndef ANDROID_STUBIFY
#include <android/log.h>
struct android_emacs_font_driver
{
jclass class;
jmethodID list;
jmethodID match;
jmethodID list_families;
jmethodID open_font;
jmethodID has_char;
jmethodID text_extents;
jmethodID encode_char;
jmethodID draw;
/* Static methods. */
jmethodID create_font_driver;
};
struct android_emacs_font_spec
{
jclass class;
jfieldID foundry;
jfieldID family;
jfieldID adstyle;
jfieldID registry;
jfieldID width;
jfieldID weight;
jfieldID slant;
jfieldID size;
jfieldID spacing;
jfieldID avgwidth;
jfieldID dpi;
};
struct android_emacs_font_metrics
{
jclass class;
jfieldID lbearing;
jfieldID rbearing;
jfieldID width;
jfieldID ascent;
jfieldID descent;
};
struct android_emacs_font_object
{
jclass class;
jfieldID min_width;
jfieldID max_width;
jfieldID pixel_size;
jfieldID height;
jfieldID space_width;
jfieldID average_width;
jfieldID ascent;
jfieldID descent;
jfieldID underline_thickness;
jfieldID underline_position;
jfieldID baseline_offset;
jfieldID relative_compose;
jfieldID default_ascent;
jfieldID encoding_charset;
jfieldID repertory_charset;
};
struct android_integer
{
jclass class;
jmethodID constructor;
jmethodID int_value;
};
struct androidfont_info
{
/* The font pseudo-vector object. */
struct font font;
/* The Java-side font. */
jobject object;
/* Cached glyph metrics arranged in a two dimensional array. */
struct font_metrics **metrics;
};
struct androidfont_entity
{
/* The font entity pvec. */
struct font_entity font;
/* The Java-side font entity. */
jobject object;
};
/* Method and class identifiers associated with the EmacsFontDriver
class. */
static struct android_emacs_font_driver font_driver_class;
/* Field and class identifiers associated with the
EmacsFontDriver$FontSpec class. */
static struct android_emacs_font_spec font_spec_class;
/* Method and class identifiers associated with the Integer class. */
static struct android_integer integer_class;
/* Field and class identifiers associated with the
EmacsFontDriver$FontMetrics class. */
static struct android_emacs_font_metrics font_metrics_class;
/* Field and class identifiers associated with the
EmacsFontDriver$FontObject class. */
static struct android_emacs_font_object font_object_class;
/* The font cache. */
static Lisp_Object font_cache;
/* The Java-side font driver. */
static jobject font_driver;
/* Initialize the class and method identifiers for functions in the
EmacsFontDriver class, and place them in `font_driver_class'. */
static void
android_init_font_driver (void)
{
jclass old;
font_driver_class.class
= (*android_java_env)->FindClass (android_java_env,
"org/gnu/emacs/EmacsFontDriver");
eassert (font_driver_class.class);
old = font_driver_class.class;
font_driver_class.class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_driver_class.class)
emacs_abort ();
#define FIND_METHOD(c_name, name, signature) \
font_driver_class.c_name \
= (*android_java_env)->GetMethodID (android_java_env, \
font_driver_class.class, \
name, signature); \
eassert (font_driver_class.c_name);
FIND_METHOD (list, "list", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
"[Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
FIND_METHOD (match, "match", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
"Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
FIND_METHOD (list_families, "listFamilies", "()[Ljava/lang/String;");
FIND_METHOD (open_font, "openFont", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
"Entity;I)Lorg/gnu/emacs/EmacsFontDriver$FontObject;");
FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
"Spec;I)I");
FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
"$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
"$FontObject;I)I");
FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
"Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
font_driver_class.create_font_driver
= (*android_java_env)->GetStaticMethodID (android_java_env,
font_driver_class.class,
"createFontDriver",
"()Lorg/gnu/emacs/"
"EmacsFontDriver;");
eassert (font_driver_class.create_font_driver);
#undef FIND_METHOD
}
/* Initialize the class and field identifiers for functions in the
EmacsFontDriver$FontSpec class, and place them in
`font_spec_class'. */
static void
android_init_font_spec (void)
{
jclass old;
font_spec_class.class
= (*android_java_env)->FindClass (android_java_env,
"org/gnu/emacs/EmacsFontDriver"
"$FontSpec");
eassert (font_spec_class.class);
old = font_spec_class.class;
font_spec_class.class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_spec_class.class)
emacs_abort ();
#define FIND_FIELD(c_name, name, signature) \
font_spec_class.c_name \
= (*android_java_env)->GetFieldID (android_java_env, \
font_spec_class.class, \
name, signature); \
eassert (font_spec_class.c_name);
FIND_FIELD (foundry, "foundry", "Ljava/lang/String;");
FIND_FIELD (family, "family", "Ljava/lang/String;");
FIND_FIELD (adstyle, "adstyle", "Ljava/lang/String;");
FIND_FIELD (registry, "registry", "Ljava/lang/String;");
FIND_FIELD (width, "width", "Ljava/lang/Integer;");
FIND_FIELD (weight, "weight", "Ljava/lang/Integer;");
FIND_FIELD (slant, "slant", "Ljava/lang/Integer;");
FIND_FIELD (size, "size", "Ljava/lang/Integer;");
FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
#undef FIND_FIELD
}
static void
android_init_font_metrics (void)
{
jclass old;
font_metrics_class.class
= (*android_java_env)->FindClass (android_java_env,
"org/gnu/emacs/EmacsFontDriver"
"$FontMetrics");
eassert (font_metrics_class.class);
old = font_metrics_class.class;
font_metrics_class.class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_metrics_class.class)
emacs_abort ();
#define FIND_FIELD(c_name, name, signature) \
font_metrics_class.c_name \
= (*android_java_env)->GetFieldID (android_java_env, \
font_metrics_class.class, \
name, signature); \
eassert (font_metrics_class.c_name);
FIND_FIELD (lbearing, "lbearing", "S");
FIND_FIELD (rbearing, "rbearing", "S");
FIND_FIELD (width, "width", "S");
FIND_FIELD (ascent, "ascent", "S");
FIND_FIELD (descent, "descent", "S");
#undef FIND_FIELD
}
static void
android_init_integer (void)
{
jclass old;
integer_class.class
= (*android_java_env)->FindClass (android_java_env,
"java/lang/Integer");
eassert (integer_class.class);
old = integer_class.class;
integer_class.class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) old);
ANDROID_DELETE_LOCAL_REF (old);
if (!integer_class.class)
emacs_abort ();
#define FIND_METHOD(c_name, name, signature) \
integer_class.c_name \
= (*android_java_env)->GetMethodID (android_java_env, \
integer_class.class, \
name, signature); \
eassert (integer_class.c_name);
FIND_METHOD (constructor, "<init>", "(I)V");
FIND_METHOD (int_value, "intValue", "()I");
#undef FIND_METHOD
}
static void
android_init_font_object (void)
{
jclass old;
font_object_class.class
= (*android_java_env)->FindClass (android_java_env,
"org/gnu/emacs/EmacsFontDriver"
"$FontObject");
eassert (font_object_class.class);
old = font_object_class.class;
font_object_class.class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_object_class.class)
emacs_abort ();
#define FIND_FIELD(c_name, name, signature) \
font_object_class.c_name \
= (*android_java_env)->GetFieldID (android_java_env, \
font_object_class.class, \
name, signature); \
eassert (font_object_class.c_name);
FIND_FIELD (min_width, "minWidth", "I");
FIND_FIELD (max_width, "maxWidth", "I");
FIND_FIELD (pixel_size, "pixelSize", "I");
FIND_FIELD (height, "height", "I");
FIND_FIELD (space_width, "spaceWidth", "I");
FIND_FIELD (average_width, "averageWidth", "I");
FIND_FIELD (ascent, "ascent", "I");
FIND_FIELD (descent, "descent", "I");
FIND_FIELD (underline_thickness, "underlineThickness", "I");
FIND_FIELD (underline_position, "underlinePosition", "I");
FIND_FIELD (baseline_offset, "baselineOffset", "I");
FIND_FIELD (relative_compose, "relativeCompose", "I");
FIND_FIELD (default_ascent, "defaultAscent", "I");
FIND_FIELD (encoding_charset, "encodingCharset", "I");
FIND_FIELD (repertory_charset, "repertoryCharset", "I");
#undef FIND_FIELD
}
static Lisp_Object
androidfont_get_cache (struct frame *frame)
{
return font_cache;
}
/* Initialize the Java side of the font driver if it has not already
been initialized. This is only done whenever necessary because the
font driver otherwise uses a lot of memory, as it has to keep every
typeface open. */
static void
androidfont_check_init (void)
{
jmethodID method;
jobject old;
if (font_driver)
return;
/* Log a loud message. This font driver really should not be
used. */
__android_log_print (ANDROID_LOG_WARN, __func__,
"The Android font driver is being used."
" Please investigate why this is so.");
method = font_driver_class.create_font_driver;
/* Initialize the font driver on the Java side. */
font_driver
= (*android_java_env)->CallStaticObjectMethod (android_java_env,
font_driver_class.class,
method);
android_exception_check ();
old = font_driver;
font_driver
= (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
ANDROID_DELETE_LOCAL_REF (old);
}
/* Return a local reference to an instance of EmacsFontDriver$FontSpec
with the same values as FONT. */
static jobject
androidfont_from_lisp (Lisp_Object font)
{
jobject spec, integer;
jstring string;
Lisp_Object tem;
spec = (*android_java_env)->AllocObject (android_java_env,
font_spec_class.class);
android_exception_check ();
#define DO_SYMBOL_FIELD(field, index) \
tem = AREF (font, index); \
if (SYMBOLP (tem)) \
{ \
/* Java seems to DTRT with the Emacs string encoding, so this does \
not matter at all. */ \
string = (*android_java_env)->NewStringUTF (android_java_env, \
SSDATA (SYMBOL_NAME (tem))); \
android_exception_check_1 (spec); \
\
(*android_java_env)->SetObjectField (android_java_env, spec, \
font_spec_class.field, \
string); \
ANDROID_DELETE_LOCAL_REF (string); \
} \
DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
#undef DO_SYMBOL_FIELD
#define DO_CARDINAL_FIELD(field, value) \
if (value != -1) \
{ \
integer = (*android_java_env)->NewObject (android_java_env, \
integer_class.class, \
integer_class.constructor, \
(jint) value); \
android_exception_check_1 (spec); \
\
(*android_java_env)->SetObjectField (android_java_env, spec, \
font_spec_class.field, \
integer); \
ANDROID_DELETE_LOCAL_REF (integer); \
}
DO_CARDINAL_FIELD (width, FONT_WIDTH_NUMERIC (font));
DO_CARDINAL_FIELD (weight, FONT_WEIGHT_NUMERIC (font));
DO_CARDINAL_FIELD (slant, FONT_SLANT_NUMERIC (font));
DO_CARDINAL_FIELD (size, (FIXNUMP (AREF (font, FONT_SIZE_INDEX))
? XFIXNUM (AREF (font, FONT_SIZE_INDEX))
: -1));
DO_CARDINAL_FIELD (spacing, (FIXNUMP (AREF (font, FONT_SPACING_INDEX))
? XFIXNUM (AREF (font, FONT_SPACING_INDEX))
: -1));
DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
: -1));
DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
? XFIXNUM (AREF (font, FONT_DPI_INDEX))
: -1));
#undef DO_CARDINAL_FIELD
return spec;
}
static void
androidfont_from_java (jobject spec, Lisp_Object entity)
{
jobject tem;
jint value;
const char *string;
#define DO_SYMBOL_FIELD(field, index) \
tem = (*android_java_env)->GetObjectField (android_java_env, \
spec, \
font_spec_class.field); \
if (tem) \
{ \
string = (*android_java_env)->GetStringUTFChars (android_java_env, \
tem, NULL); \
if (!string) \
memory_full (0); \
ASET (entity, index, intern (string)); \
(*android_java_env)->ReleaseStringUTFChars (android_java_env, \
tem, string); \
ANDROID_DELETE_LOCAL_REF (tem); \
}
DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
#undef DO_SYMBOL_FIELD
#define DO_CARDINAL_FIELD(field, index, is_style) \
tem = (*android_java_env)->GetObjectField (android_java_env, \
spec, \
font_spec_class.field); \
if (tem) \
{ \
value \
= (*android_java_env)->CallIntMethod (android_java_env, \
tem, \
integer_class.int_value); \
if (!is_style) \
ASET (entity, index, make_fixnum (value)); \
else \
FONT_SET_STYLE (entity, index, make_fixnum (value)); \
ANDROID_DELETE_LOCAL_REF (tem); \
}
DO_CARDINAL_FIELD (width, FONT_WIDTH_INDEX, true);
DO_CARDINAL_FIELD (weight, FONT_WEIGHT_INDEX, true);
DO_CARDINAL_FIELD (slant, FONT_SLANT_INDEX, true);
DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
#undef DO_CARDINAL_FIELD
}
/* Transfer the values from FONT, which must be some kind of font
entity, */
static Lisp_Object
androidfont_list (struct frame *f, Lisp_Object font_spec)
{
jobject spec, array, tem;
jarray entities;
jsize i, size;
Lisp_Object value, entity;
struct androidfont_entity *info;
/* Maybe initialize the font driver. */
androidfont_check_init ();
spec = androidfont_from_lisp (font_spec);
array = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.list,
spec);
android_exception_check_1 (spec);
ANDROID_DELETE_LOCAL_REF (spec);
entities = (jarray) array;
size = (*android_java_env)->GetArrayLength (android_java_env,
entities);
value = Qnil;
for (i = 0; i < size; ++i)
{
entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
info = (struct androidfont_entity *) XFONT_ENTITY (entity);
/* The type must be set correctly, or font_open_entity won't be
able to find the right font driver. */
ASET (entity, FONT_TYPE_INDEX, Qandroid);
/* Clear this now in case GC happens without it set, which can
happen if androidfont_from_java runs out of memory. */
info->object = NULL;
tem = (*android_java_env)->GetObjectArrayElement (android_java_env,
entities, i);
androidfont_from_java (tem, entity);
/* Now, make a global reference to the Java font entity. */
info->object = (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) tem);
android_exception_check_2 (tem, entities);
ANDROID_DELETE_LOCAL_REF (tem);
value = Fcons (entity, value);
}
ANDROID_DELETE_LOCAL_REF (entities);
return Fnreverse (value);
}
static Lisp_Object
androidfont_match (struct frame *f, Lisp_Object font_spec)
{
jobject spec, result;
Lisp_Object entity;
struct androidfont_entity *info;
/* Maybe initialize the font driver. */
androidfont_check_init ();
spec = androidfont_from_lisp (font_spec);
result = (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.match,
spec);
android_exception_check_1 (spec);
ANDROID_DELETE_LOCAL_REF (spec);
entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
info = (struct androidfont_entity *) XFONT_ENTITY (entity);
/* The type must be set correctly, or font_open_entity won't be able
to find the right font driver. */
ASET (entity, FONT_TYPE_INDEX, Qandroid);
info->object = NULL;
androidfont_from_java (result, entity);
info->object = (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) result);
android_exception_check_1 (result);
ANDROID_DELETE_LOCAL_REF (result);
return entity;
}
static int
androidfont_draw (struct glyph_string *s, int from, int to,
int x, int y, bool with_background)
{
struct androidfont_info *info;
jarray chars;
int rc;
jobject gcontext, drawable;
/* Maybe initialize the font driver. */
androidfont_check_init ();
verify (sizeof (unsigned int) == sizeof (jint));
info = (struct androidfont_info *) s->font;
gcontext = android_resolve_handle (s->gc->gcontext);
drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f));
chars = (*android_java_env)->NewIntArray (android_java_env,
to - from);
android_exception_check ();
(*android_java_env)->SetIntArrayRegion (android_java_env, chars,
0, to - from,
(jint *) s->char2b + from);
info = (struct androidfont_info *) s->font;
prepare_face_for_display (s->f, s->face);
rc = (*android_java_env)->CallIntMethod (android_java_env,
font_driver,
font_driver_class.draw,
info->object,
gcontext, drawable,
chars, (jint) x, (jint) y,
(jint) s->width,
(jboolean) with_background);
android_exception_check_1 (chars);
ANDROID_DELETE_LOCAL_REF (chars);
return rc;
}
static Lisp_Object
androidfont_open_font (struct frame *f, Lisp_Object font_entity,
int pixel_size)
{
struct androidfont_info *font_info;
struct androidfont_entity *entity;
struct font *font;
Lisp_Object font_object;
jobject old;
jint value;
/* Maybe initialize the font driver. */
androidfont_check_init ();
if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
else if (pixel_size == 0)
{
/* This bit was copied from xfont.c. The values might need
adjustment. */
if (FRAME_FONT (f))
pixel_size = FRAME_FONT (f)->pixel_size;
else
pixel_size = 12;
}
entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
block_input ();
font_object = font_make_object (VECSIZE (struct androidfont_info),
font_entity, pixel_size);
ASET (font_object, FONT_TYPE_INDEX, Qandroid);
font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
font = &font_info->font;
font->driver = &androidfont_driver;
/* Clear font_info->object and font_info->metrics early in case GC
happens later on! */
font_info->object = NULL;
font_info->metrics = NULL;
unblock_input ();
font_info->object
= (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.open_font,
entity->object,
(jint) pixel_size);
android_exception_check ();
old = font_info->object;
font_info->object
= (*android_java_env)->NewGlobalRef (android_java_env, old);
android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_info->object)
return Qnil;
/* Copy the font attributes from the Java object. */
androidfont_from_java (font_info->object, font_object);
/* Copy font attributes inside EmacsFontDriver$FontObject. */
#define DO_CARDINAL_FIELD(field) \
value \
= (*android_java_env)->GetIntField (android_java_env, \
font_info->object, \
font_object_class.field); \
font->field = value;
DO_CARDINAL_FIELD (min_width);
DO_CARDINAL_FIELD (max_width);
DO_CARDINAL_FIELD (pixel_size);
DO_CARDINAL_FIELD (height);
DO_CARDINAL_FIELD (space_width);
DO_CARDINAL_FIELD (average_width);
DO_CARDINAL_FIELD (ascent);
DO_CARDINAL_FIELD (descent);
DO_CARDINAL_FIELD (underline_thickness);
DO_CARDINAL_FIELD (underline_position);
DO_CARDINAL_FIELD (baseline_offset);
DO_CARDINAL_FIELD (relative_compose);
DO_CARDINAL_FIELD (default_ascent);
DO_CARDINAL_FIELD (encoding_charset);
DO_CARDINAL_FIELD (repertory_charset);
#undef DO_CARDINAL_FIELD
/* This should eventually become unnecessary. */
font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt);
return font_object;
}
static void
androidfont_close_font (struct font *font)
{
struct androidfont_info *info;
int i;
/* Maybe initialize the font driver. */
androidfont_check_init ();
info = (struct androidfont_info *) font;
/* Free the font metrics cache if it exists. */
if (info->metrics)
{
for (i = 0; i < 256; ++i)
xfree (info->metrics[i]);
xfree (info->metrics);
}
info->metrics = NULL;
/* If info->object is NULL, then FONT was unsuccessfully created,
and there is no global reference that has to be deleted.
Alternatively, FONT may have been closed by font_close_object,
with this function called from GC. */
if (!info->object)
return;
(*android_java_env)->DeleteGlobalRef (android_java_env,
info->object);
info->object = NULL;
}
static int
androidfont_has_char (Lisp_Object font, int c)
{
struct androidfont_info *info;
struct androidfont_entity *entity;
/* Maybe initialize the font driver. */
androidfont_check_init ();
if (FONT_ENTITY_P (font))
{
entity = (struct androidfont_entity *) XFONT_ENTITY (font);
return (*android_java_env)->CallIntMethod (android_java_env,
font_driver,
font_driver_class.has_char,
entity->object, (jint) c);
}
else
{
info = (struct androidfont_info *) XFONT_OBJECT (font);
return (*android_java_env)->CallIntMethod (android_java_env,
font_driver,
font_driver_class.has_char,
info->object, (jint) c);
}
}
static unsigned
androidfont_encode_char (struct font *font, int c)
{
struct androidfont_info *info;
/* Maybe initialize the font driver. */
androidfont_check_init ();
info = (struct androidfont_info *) font;
return (*android_java_env)->CallIntMethod (android_java_env,
font_driver,
font_driver_class.encode_char,
info->object, (jchar) c);
}
static void
androidfont_cache_text_extents (struct androidfont_info *info,
unsigned int glyph,
struct font_metrics *metrics)
{
int i;
/* Glyphs larger than 65535 can't be cached. */
if (glyph >= 256 * 256)
return;
if (!info->metrics)
info->metrics = xzalloc (256 * sizeof *info->metrics);
if (!info->metrics[glyph / 256])
{
info->metrics[glyph / 256]
= xnmalloc (256, sizeof **info->metrics);
/* Now, all the metrics in that array as invalid by setting
lbearing to SHRT_MAX. */
for (i = 0; i < 256; ++i)
info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
}
/* Finally, cache the glyph. */
info->metrics[glyph / 256][glyph % 256] = *metrics;
}
static bool
androidfont_check_cached_extents (struct androidfont_info *info,
unsigned int glyph,
struct font_metrics *metrics)
{
if (info->metrics && info->metrics[glyph / 256]
&& info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
{
*metrics = info->metrics[glyph / 256][glyph % 256];
return true;
}
return false;
}
static void
androidfont_text_extents (struct font *font, const unsigned int *code,
int nglyphs, struct font_metrics *metrics)
{
struct androidfont_info *info;
jarray codepoint_array;
jobject metrics_object;
short value;
/* Maybe initialize the font driver. */
androidfont_check_init ();
info = (struct androidfont_info *) font;
if (nglyphs == 1
&& androidfont_check_cached_extents (info, *code, metrics))
return;
/* Allocate the arrays of code points and font metrics. */
codepoint_array
= (*android_java_env)->NewIntArray (android_java_env,
nglyphs);
if (!codepoint_array)
{
(*android_java_env)->ExceptionClear (android_java_env);
memory_full (0);
}
verify (sizeof (unsigned int) == sizeof (jint));
/* Always true on every Android device. */
(*android_java_env)->SetIntArrayRegion (android_java_env,
codepoint_array,
0, nglyphs,
(jint *) code);
metrics_object
= (*android_java_env)->AllocObject (android_java_env,
font_metrics_class.class);
(*android_java_env)->CallVoidMethod (android_java_env,
font_driver,
font_driver_class.text_extents,
info->object, codepoint_array,
metrics_object);
if ((*android_java_env)->ExceptionCheck (android_java_env))
{
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (metrics_object);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
memory_full (0);
}
#define DO_CARDINAL_FIELD(field) \
value \
= (*android_java_env)->GetShortField (android_java_env, \
metrics_object, \
font_metrics_class.field); \
metrics->field = value;
DO_CARDINAL_FIELD (lbearing);
DO_CARDINAL_FIELD (rbearing);
DO_CARDINAL_FIELD (width);
DO_CARDINAL_FIELD (ascent);
DO_CARDINAL_FIELD (descent);
#undef DO_CARDINAL_FIELD
ANDROID_DELETE_LOCAL_REF (metrics_object);
ANDROID_DELETE_LOCAL_REF (codepoint_array);
/* Emacs spends a lot of time in androidfont_text_extents, which
makes calling JNI too slow. Cache the metrics for this single
glyph. */
if (nglyphs == 1)
androidfont_cache_text_extents (info, *code, metrics);
}
static Lisp_Object
androidfont_list_family (struct frame *f)
{
Lisp_Object families;
jarray family_array;
jobject string;
jsize i, length;
const char *family;
/* Return if the Android font driver is not initialized. Loading
every font under Android takes a non trivial amount of memory,
and is not something that should be done when the user tries to
list all of the font families. */
if (!font_driver)
return Qnil;
family_array
= (*android_java_env)->CallObjectMethod (android_java_env,
font_driver,
font_driver_class.list_families);
android_exception_check ();
length = (*android_java_env)->GetArrayLength (android_java_env,
family_array);
families = Qnil;
for (i = 0; i < length; ++i)
{
string = (*android_java_env)->GetObjectArrayElement (android_java_env,
family_array, i);
family = (*android_java_env)->GetStringUTFChars (android_java_env,
(jstring) string, NULL);
if (!family)
{
ANDROID_DELETE_LOCAL_REF (string);
ANDROID_DELETE_LOCAL_REF (family_array);
}
families = Fcons (build_string_from_utf8 (string), families);
(*android_java_env)->ReleaseStringUTFChars (android_java_env,
(jstring) string,
family);
ANDROID_DELETE_LOCAL_REF (string);
}
ANDROID_DELETE_LOCAL_REF (family_array);
return Fnreverse (families);
}
struct font_driver androidfont_driver =
{
.type = LISPSYM_INITIALLY (Qandroid),
.case_sensitive = true,
.get_cache = androidfont_get_cache,
.list = androidfont_list,
.match = androidfont_match,
.draw = androidfont_draw,
.open_font = androidfont_open_font,
.close_font = androidfont_close_font,
.has_char = androidfont_has_char,
.encode_char = androidfont_encode_char,
.text_extents = androidfont_text_extents,
.list_family = androidfont_list_family,
};
static void
syms_of_androidfont_for_pdumper (void)
{
register_font_driver (&androidfont_driver, NULL);
}
void
syms_of_androidfont (void)
{
DEFSYM (Qfontsize, "fontsize");
pdumper_do_now_and_after_load (syms_of_androidfont_for_pdumper);
font_cache = list (Qnil);
staticpro (&font_cache);
}
void
init_androidfont (void)
{
if (!android_init_gui)
return;
android_init_font_driver ();
android_init_font_spec ();
android_init_font_metrics ();
android_init_font_object ();
android_init_integer ();
/* The Java font driver is not initialized here because it uses a lot
of memory. */
}
void
android_finalize_font_entity (struct font_entity *entity)
{
struct androidfont_entity *info;
info = (struct androidfont_entity *) entity;
if (info->object)
(*android_java_env)->DeleteGlobalRef (android_java_env,
info->object);
/* Not sure if this can be called twice. */
info->object = NULL;
}
#endif