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 may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/* 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