1103 lines
30 KiB
C
1103 lines
30 KiB
C
/* 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
|