From 494bedde3235f9034746c977260bbbc2c1e51d8c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 11 Jan 2023 16:06:15 +0800 Subject: [PATCH] Bring up the sfnt-android font driver * configure.ac (ANDROID_CFLAGS): Add sfnt-related font objects to ANDROID_OBJ when not building stubs. * lisp/startup.el (android-fonts-enumerated): New variable. (normal-top-level): Set it. Also enumerate fonts as early as possible upon startup. * src/alloc.c (cleanup_vector): Only finalize Android font entities. (garbage_collect): Mark sfntfont.c. * src/android.c (struct android_emacs_drawable): New field `damage_rect'. (android_init_emacs_drawable): Initialize Lorg/gnu/emacs/EmacsDrawable;#damageRect(Landroid/graphics/rect;)V. (android_create_gc): Initialize cached GC fields. (android_free_gc): Free cached GC clip rectangles. (android_change_gc): Cache fields as appropriate. (android_set_clip_rectangles): Set cached clip rectangles for easy access from C. (android_get_gc_values): Use cached values. (android_get_image): Remove obsolete comment. (android_lock_bitmap, android_damage_window): New functions that don't parallel anything on X. * src/android.h: Update prototypes. * src/androidfns.c (android_default_font_parameter): Set Droid Sans Mono as the default monospace font. (Fx_create_frame): Register the sfntfont driver. * src/androidgui.h (struct android_gc): Add C side caches for clip rectangles and the foreground and background colors. * src/androidterm.h: Update prototypes. * src/dispextern.h (struct gui_box): New struct. (gui_union_rectangles): New function. * src/emacs.c (android_emacs_init): Initialize Android font stuff late. * src/font.c (font_make_entity): Clear `is_android' field on Android. (font_make_entity_android): Set `is_android' field. * src/font.h (struct font_entity): New field `is_android'. * src/print.c (print_vectorlike): Don't print private data, which could include Lisp_Misc. * src/sfnt.c (sfnt_read_cmap_format_0, sfnt_read_cmap_format_2) (sfnt_read_cmap_format_4, sfnt_read_cmap_format_6) (sfnt_read_cmap_format_8, sfnt_read_cmap_format_12): Remove buggy pragmas. (sfnt_lookup_glyph_4_1): New function. (sfnt_lookup_glyph_4): Handle malformed lookup tables found on Android. (sfnt_lookup_glyph): Fix overflow problems in glyph checks. (sfnt_read_glyph): Handle empty glyphs. This implements some behavior which everyone else seems to as well, but I can't find documented in the TrueType Reference Manual. (sfnt_free_glyph): Export correctly. (sfnt_transform_coordinates): Make faster. (sfnt_lerp_half): Fix lerping in some cases. (sfnt_decompose_glyph): Handle empty glyphs correctly. Close contours manually instead of waiting for the edge building process to do that. This lets curves be handled correctly. (struct sfnt_build_glyph_outline_context): Move internal struct back to sfnt.c. (sfnt_build_append): Fix detection of initial entry. (sfnt_curve_to_and_build_1): Fix De Casteljau implementation. (sfnt_curve_to_and_build): Use fixed point arithmetic to scale outlines. (sfnt_build_glyph_outline): Clear reference counts. Use fixed point arithmetic. (sfnt_prepare_raster): Align rasters to 4 bytes, SFNT_POLY_ALIGNMENT. Fix calculation of offx and offy. (sfnt_step_edge_by): Step edge by previously computed step_x. (sfnt_build_outline_edges): Adjust for already closed contours. Ignore edges abandoned after grid fit. Also precompute step_x to avoid multiplication on each span rastered. (sfnt_poly_edges): Improve alignment. (sfnt_fill_span): Rewrite to avoid control flow in while loop. (sfnt_poly_span): Remove unnecessary code. (sfnt_raster_glyph_outline): Use raster stride instead of width. (sfnt_test_edge, sfnt_test_raster, main): Improve debugging code. * src/sfnt.h (struct sfnt_glyph_outline): Add refcount field to outline. (struct sfnt_build_glyph_outline_context): Remove private struct. (struct sfnt_raster): Add refcount field to raster. (struct sfnt_edge): Improve doc. Add `source_x' field used when built with TEST. (SFNT_CEIL_FIXED): New macro. * src/sfntfont-android.c (sfntfont_android_saturate32) (sfntfont_android_scale32, sfntfont_android_mul8x2) (sfntfont_android_blend, U255TO256) (sfntfont_android_composite_bitmap, sfntfont_android_union_boxes) (sfntfont_android_put_glyphs, sfntfont_android_get_cache): New functions. (android_sfntfont_driver): New font driver. (Fandroid_enumerate_fonts): New function. (syms_of_sfntfont_android_for_pdumper, init_sfntfont_android) (syms_of_sfntfont_android): Initialize default fonts, special family mapping and font driver. * src/sfntfont.c (struct sfnt_font_desc): New fields `char_cache', `cmap_invalid' and `subtable'. (sfnt_setup_coding_system): Improve commentary. Add default branch. Fix return value. (sfnt_safe_encode_coding_object_1) (sfnt_safe_encode_coding_object_2): (sfnt_safe_encode_coding_object): Use decode_coding_object instead of encode_coding_object. (sfnt_decode_font_string): Adjust for rename. (sfnt_decode_foundry_name): New function. (sfnt_weight_descriptions, sfnt_slant_descriptions) (sfnt_width_descriptions): Fix definitions. (sfnt_parse_style): Make function work. (sfnt_enum_font): Initialize designer, char-cache and subtable platform ID. (sfntfont_charset_for_name, mark_sfntfont) (sfntfont_charset_for_cmap): New functions. (syms_of_sfntfont): New variable `sfnt-default-family-alist'. * src/sfntfont.h (_SFNTFONT_H_): Update prototypes. * src/xdisp.c (gui_union_rectangles): New function. --- configure.ac | 9 +- lisp/startup.el | 13 + src/alloc.c | 12 +- src/android.c | 153 +++- src/android.h | 7 + src/androidfns.c | 4 +- src/androidgui.h | 13 + src/androidterm.h | 2 + src/dispextern.h | 19 + src/emacs.c | 22 +- src/font.c | 10 + src/font.h | 5 + src/print.c | 17 +- src/sfnt.c | 490 +++++++---- src/sfnt.h | 55 +- src/sfntfont-android.c | 489 ++++++++++- src/sfntfont.c | 1822 +++++++++++++++++++++++++++++++++++++++- src/sfntfont.h | 33 + src/xdisp.c | 49 ++ 19 files changed, 2967 insertions(+), 257 deletions(-) diff --git a/configure.ac b/configure.ac index b348c0ad005..68de5f02b97 100644 --- a/configure.ac +++ b/configure.ac @@ -1360,6 +1360,9 @@ AS_IF([test $gl_gcc_warnings = no], nw="$nw -Wsync-nand" # irrelevant here, and provokes ObjC warning nw="$nw -Wunsafe-loop-optimizations" # OK to suppress unsafe optimizations nw="$nw -Wbad-function-cast" # These casts are no worse than others. + nw="$nw -Wunknown-warning-option" # Let #pragma GCC ignore work properly + # even when the compiler in use doesn't + # support the option. # Emacs doesn't care about shadowing; see # . @@ -2250,12 +2253,16 @@ for Android, but all API calls need to be stubbed out]) # requires Emacs be built as a position independent executable. ANDROID_CFLAGS="-fPIC -fvisibility=hidden" + # Graphics code in sfntfont-android.c benefits heavily from + # vectorization. + ANDROID_CFLAGS="$ANDROID_CFLAGS -ftree-vectorize" + # Link with libraries required for Android support. ANDROID_LIBS="-landroid -llog -ljnigraphics" # Link with the sfnt font library and sfntfont.o, along with # sfntfont-android.o. - ANDROID_OBJ="sfnt.o sfntfont.o sfntfont-android.o" + ANDROID_OBJ="$ANDROID_OBJ sfnt.o sfntfont.o sfntfont-android.o" fi fi diff --git a/lisp/startup.el b/lisp/startup.el index fa84985580e..5eb53ecc6d1 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -574,11 +574,24 @@ the updated value." (setq startup--original-eln-load-path (copy-sequence native-comp-eln-load-path)))) +(defvar android-fonts-enumerated nil + "Whether or not fonts have been enumerated already. +On Android, Emacs uses this variable internally at startup.") + (defun normal-top-level () "Emacs calls this function when it first starts up. It sets `command-line-processed', processes the command-line, reads the initialization files, etc. It is the default value of the variable `top-level'." + ;; Initialize the Android font driver late. + ;; This is done here because it needs the `mac-roman' coding system + ;; to be loaded. + (when (and (featurep 'android) + (fboundp 'android-enumerate-fonts) + (not android-fonts-enumerated)) + (funcall 'android-enumerate-fonts) + (setq android-fonts-enumerated t)) + ;; Allow disabling automatic .elc->.eln processing. (setq inhibit-automatic-native-compilation (getenv "EMACS_INHIBIT_AUTOMATIC_NATIVE_COMPILATION")) diff --git a/src/alloc.c b/src/alloc.c index 65a49108d86..86e019b931b 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -50,6 +50,10 @@ along with GNU Emacs. If not, see . */ #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#include "sfntfont.h" +#endif + #ifdef HAVE_TREE_SITTER #include "treesit.h" #endif @@ -3346,8 +3350,9 @@ cleanup_vector (struct Lisp_Vector *vector) #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY /* The Android font driver needs the ability to associate extra information with font entities. */ - if ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) - == FONT_ENTITY_MAX) + if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK) + == FONT_ENTITY_MAX) + && PSEUDOVEC_STRUCT (vector, font_entity)->is_android) android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity)); #endif } @@ -6477,6 +6482,9 @@ garbage_collect (void) #ifdef HAVE_ANDROID mark_androidterm (); +#ifndef ANDROID_STUBIFY + mark_sfntfont (); +#endif #endif #ifdef HAVE_NS diff --git a/src/android.c b/src/android.c index e2056b666d6..2f852662001 100644 --- a/src/android.c +++ b/src/android.c @@ -101,6 +101,7 @@ struct android_emacs_drawable { jclass class; jmethodID get_bitmap; + jmethodID damage_rect; }; /* The asset manager being used. */ @@ -1102,6 +1103,7 @@ android_init_emacs_drawable (void) assert (drawable_class.c_name); FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;"); + FIND_METHOD (damage_rect, "damageRect", "(Landroid/graphics/Rect;)V"); #undef FIND_METHOD } @@ -1743,6 +1745,12 @@ android_create_gc (enum android_gc_value_mask mask, gc = xmalloc (sizeof *gc); prev_max_handle = max_handle; gc->gcontext = android_alloc_id (); + gc->foreground = 0; + gc->background = 0xffffff; + gc->clip_rects = NULL; + + /* This means to not apply any clipping. */ + gc->num_clip_rects = -1; if (!gc->gcontext) { @@ -1780,6 +1788,8 @@ void android_free_gc (struct android_gc *gc) { android_destroy_handle (gc->gcontext); + + xfree (gc->clip_rects); xfree (gc); } @@ -1795,16 +1805,22 @@ android_change_gc (struct android_gc *gc, ANDROID_HANDLE_GCONTEXT); if (mask & ANDROID_GC_FOREGROUND) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_foreground, - values->foreground); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_foreground, + values->foreground); + gc->foreground = values->foreground; + } if (mask & ANDROID_GC_BACKGROUND) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_background, - values->background); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_background, + values->background); + gc->background = values->background; + } if (mask & ANDROID_GC_FUNCTION) (*android_java_env)->SetIntField (android_java_env, @@ -1838,6 +1854,10 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_rects, NULL); + + xfree (gc->clip_rects); + gc->clip_rects = NULL; + gc->num_clip_rects = -1; } if (mask & ANDROID_GC_STIPPLE) @@ -1943,6 +1963,19 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin, (*android_java_env)->CallVoidMethod (android_java_env, gcontext, emacs_gc_mark_dirty); + + /* Cache the clip rectangles on the C side for + sfntfont-android.c. */ + if (gc->clip_rects) + xfree (gc->clip_rects); + + /* If gc->num_clip_rects is 0, then no drawing will be performed at + all. */ + gc->clip_rects = xmalloc (sizeof *gc->clip_rects + * n_clip_rects); + gc->num_clip_rects = n_clip_rects; + memcpy (gc->clip_rects, clip_rects, + n_clip_rects * sizeof *gc->clip_rects); } void @@ -2098,16 +2131,10 @@ android_get_gc_values (struct android_gc *gc, if (mask & ANDROID_GC_FOREGROUND) /* GCs never have 32 bit colors, so we don't have to worry about sign extension here. */ - values->foreground - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_foreground); + values->foreground = gc->foreground; if (mask & ANDROID_GC_BACKGROUND) - values->background - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_background); + values->background = gc->background; if (mask & ANDROID_GC_FUNCTION) values->function @@ -2657,8 +2684,6 @@ android_get_image (android_drawable handle, unsigned char *data1, *data2; int i, x; - /* N.B. that supporting windows requires some more work to make - EmacsDrawable.getBitmap thread safe. */ drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW, ANDROID_HANDLE_PIXMAP); @@ -2881,6 +2906,98 @@ android_put_image (android_pixmap handle, struct android_image *image) +/* Low level drawing primitives. */ + +/* Lock the bitmap corresponding to the window WINDOW. Return the + bitmap data upon success, and store the bitmap object in + BITMAP_RETURN. Value is NULL upon failure. + + The caller must take care to unlock the bitmap data afterwards. */ + +unsigned char * +android_lock_bitmap (android_window window, + AndroidBitmapInfo *bitmap_info, + jobject *bitmap_return) +{ + jobject drawable, bitmap; + void *data; + + drawable = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + + /* Look up the drawable and get the bitmap corresponding to it. + Then, lock the bitmap's bits. */ + bitmap = (*android_java_env)->CallObjectMethod (android_java_env, + drawable, + drawable_class.get_bitmap); + if (!bitmap) + /* NULL is returned when the bitmap does not currently exist due + to ongoing reconfiguration on the main thread. */ + return NULL; + + memset (bitmap_info, 0, sizeof *bitmap_info); + + /* Get the bitmap info. */ + AndroidBitmap_getInfo (android_java_env, bitmap, bitmap_info); + + if (!bitmap_info->stride) + { + ANDROID_DELETE_LOCAL_REF (bitmap); + return NULL; + } + + /* Now lock the image data. */ + data = NULL; + AndroidBitmap_lockPixels (android_java_env, bitmap, &data); + + if (!data) + { + ANDROID_DELETE_LOCAL_REF (bitmap); + return NULL; + } + + /* Give the bitmap to the caller. */ + *bitmap_return = bitmap; + + /* The bitmap data is now locked. */ + return data; +} + +/* Damage the window HANDLE by the given damage rectangle. */ + +void +android_damage_window (android_drawable handle, + struct android_rectangle *damage) +{ + jobject drawable, rect; + + drawable = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); + + /* Now turn DAMAGE into a Java rectangle. */ + rect = (*android_java_env)->NewObject (android_java_env, + android_rect_class, + android_rect_constructor, + (jint) damage->x, + (jint) damage->y, + (jint) (damage->x + + damage->width), + (jint) (damage->y + + damage->height)); + if (!rect) + { + (*android_java_env)->ExceptionClear (android_java_env); + memory_full (0); + } + + /* Post the damage to the drawable. */ + (*android_java_env)->CallVoidMethod (android_java_env, + drawable, + drawable_class.damage_rect, + rect); + ANDROID_DELETE_LOCAL_REF (rect); +} + + + #undef faccessat /* Replace the system faccessat with one which understands AT_EACCESS. diff --git a/src/android.h b/src/android.h index 4d702fe2079..4cf194f23cf 100644 --- a/src/android.h +++ b/src/android.h @@ -29,6 +29,8 @@ along with GNU Emacs. If not, see . */ #include #include +#include + #include "androidgui.h" #endif @@ -64,6 +66,11 @@ enum android_handle_type extern jobject android_resolve_handle (android_handle, enum android_handle_type); +extern unsigned char *android_lock_bitmap (android_window, + AndroidBitmapInfo *, + jobject *); +extern void android_damage_window (android_window, + struct android_rectangle *); #endif diff --git a/src/androidfns.c b/src/androidfns.c index 7fdb2f14141..96c2746a21a 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -531,8 +531,7 @@ android_default_font_parameter (struct frame *f, Lisp_Object parms) if (! FONTP (font) && ! STRINGP (font)) { const char *names[] = { - /* This will find the normal font. The default font size on - Android is 8. */ + "Droid Sans Mono", "monospace", "DroidSansMono", NULL @@ -772,6 +771,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, } register_font_driver (&androidfont_driver, f); + register_font_driver (&android_sfntfont_driver, f); image_cache_refcount = (FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount diff --git a/src/androidgui.h b/src/androidgui.h index d51b73a764d..7d045f7b450 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -116,8 +116,21 @@ struct android_gc_values struct android_gc { + /* Array of clip rectangles. */ + struct android_rectangle *clip_rects; + + /* Number of clip rectangles. When -1, it means clipping should not + be applied. */ + int num_clip_rects; + /* The Java-side handle. */ android_gcontext gcontext; + + /* Current foreground color. */ + unsigned long foreground; + + /* Current background color. */ + unsigned long background; }; enum android_swap_action diff --git a/src/androidterm.h b/src/androidterm.h index 562dcdead17..814bcf9f08c 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -376,6 +376,8 @@ extern void android_finalize_font_entity (struct font_entity *); /* Defined in sfntfont-android.c. */ +extern const struct font_driver android_sfntfont_driver; + extern void init_sfntfont_android (void); extern void syms_of_sfntfont_android (void); diff --git a/src/dispextern.h b/src/dispextern.h index 770d29f867a..2ceed53813e 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -177,6 +177,22 @@ typedef void *Emacs_Cursor; #define NativeRectangle int #endif +#ifdef HAVE_WINDOW_SYSTEM + +/* ``box'' structure similar to that found in the X sample server, + meaning that X2 and Y2 are not actually the end of the box, but one + pixel past the end of the box, which makes checking for overlaps + less necessary. This is convenient to use in every GUI port. */ + +struct gui_box +{ + /* Bounds of the box. */ + int x1, y1; + int x2, y2; +}; + +#endif + /* Text cursor types. */ enum text_cursor_kinds @@ -3525,6 +3541,9 @@ extern void expose_frame (struct frame *, int, int, int, int); extern bool gui_intersect_rectangles (const Emacs_Rectangle *, const Emacs_Rectangle *, Emacs_Rectangle *); +extern void gui_union_rectangles (const Emacs_Rectangle *, + const Emacs_Rectangle *, + Emacs_Rectangle *); extern void gui_consider_frame_title (Lisp_Object); #endif /* HAVE_WINDOW_SYSTEM */ diff --git a/src/emacs.c b/src/emacs.c index e287ba53e13..e3f6c7d66f7 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2465,12 +2465,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem init_haiku_select (); #endif -#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY - init_androidfont (); - init_sfntfont (); - init_sfntfont_android (); -#endif - init_charset (); /* This calls putenv and so must precede init_process_emacs. */ @@ -2505,6 +2499,12 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem init_window (); init_font (); +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + init_androidfont (); + init_sfntfont (); + init_sfntfont_android (); +#endif + if (!initialized) { char *file; @@ -2559,6 +2559,16 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem safe_run_hooks (Qafter_pdump_load_hook); #endif +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && 0 + /* This comes very late in the startup process because it requires + most of lisp/international to be loaded. This approach doesn't + work because normal-top-level runs and creates the initial frame + before fonts are initialized. So this is done in + normal-top-level instead. */ + Vtop_level = list3 (Qprogn, Vtop_level, + list1 (Qandroid_enumerate_fonts)); +#endif + /* Enter editor command loop. This never returns. */ set_initial_minibuffer_mode (); Frecursive_edit (); diff --git a/src/font.c b/src/font.c index b90013d3afc..bf561095ef7 100644 --- a/src/font.c +++ b/src/font.c @@ -177,6 +177,11 @@ font_make_entity (void) allocate_pseudovector (VECSIZE (struct font_entity), FONT_ENTITY_MAX, FONT_ENTITY_MAX, PVEC_FONT)); XSETFONT (font_entity, entity); + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + entity->is_android = false; +#endif + return font_entity; } @@ -190,6 +195,11 @@ font_make_entity_android (int size) = ((struct font_entity *) allocate_pseudovector (size, FONT_ENTITY_MAX, FONT_ENTITY_MAX, PVEC_FONT)); + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + entity->is_android = true; +#endif + XSETFONT (font_entity, entity); return font_entity; } diff --git a/src/font.h b/src/font.h index 297ec4c1e7d..ed3b17db994 100644 --- a/src/font.h +++ b/src/font.h @@ -260,6 +260,11 @@ struct font_entity { union vectorlike_header header; Lisp_Object props[FONT_ENTITY_MAX]; + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Whether or not this is an Android font entity. */ + bool is_android; +#endif }; /* A value which may appear in the member `encoding' of struct font diff --git a/src/print.c b/src/print.c index d4a9ff89246..bc6d5487c13 100644 --- a/src/print.c +++ b/src/print.c @@ -1913,12 +1913,17 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, print_c_string ("# FONT_WIDTH_INDEX) - print_object (AREF (obj, i), printcharfun, escapeflag); - else - print_object (font_style_symbolic (obj, i, 0), - printcharfun, escapeflag); + /* FONT_EXTRA_INDEX can contain private information in + font entities which isn't safe to print. */ + if (i != FONT_EXTRA_INDEX || !FONT_ENTITY_P (obj)) + { + printchar (' ', printcharfun); + if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX) + print_object (AREF (obj, i), printcharfun, escapeflag); + else + print_object (font_style_symbolic (obj, i, 0), + printcharfun, escapeflag); + } } } else diff --git a/src/sfnt.c b/src/sfnt.c index dfdc4f7acf8..20cf2376d85 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -34,6 +34,10 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include +#if defined __GNUC__ && !defined __clang__ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + #ifdef TEST #include @@ -221,17 +225,12 @@ sfnt_read_cmap_format_0 (int fd, format0->format = header->format; format0->length = header->length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the rest. */ wanted_size = (sizeof *format0 - offsetof (struct sfnt_cmap_format_0, language)); rc = read (fd, &format0->language, wanted_size); -#pragma GCC diagnostic pop - if (rc < wanted_size) { xfree (format0); @@ -267,9 +266,6 @@ sfnt_read_cmap_format_2 (int fd, format2->format = header->format; format2->length = header->length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the part before the variable length data. */ min_bytes -= offsetof (struct sfnt_cmap_format_2, language); rc = read (fd, &format2->language, min_bytes); @@ -313,8 +309,6 @@ sfnt_read_cmap_format_2 (int fd, return (struct sfnt_cmap_format_2 *) -1; } -#pragma GCC diagnostic pop - /* Check whether or not the data is of the correct size. */ if (min_bytes < nsub * sizeof *format2->subheaders) { @@ -377,9 +371,6 @@ sfnt_read_cmap_format_4 (int fd, format4->format = header->format; format4->length = header->length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the initial data. */ min_bytes -= offsetof (struct sfnt_cmap_format_4, language); rc = read (fd, &format4->language, min_bytes); @@ -453,8 +444,6 @@ sfnt_read_cmap_format_4 (int fd, for (i = 0; i < format4->glyph_index_size; ++i) sfnt_swap16 (&format4->glyph_index_array[i]); -#pragma GCC diagnostic pop - /* Done. Return the format 4 character map. */ return format4; } @@ -486,9 +475,6 @@ sfnt_read_cmap_format_6 (int fd, format6->format = header->format; format6->length = header->length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the fixed size data. */ min_size -= offsetof (struct sfnt_cmap_format_6, language); rc = read (fd, &format6->language, min_size); @@ -522,8 +508,6 @@ sfnt_read_cmap_format_6 (int fd, return (struct sfnt_cmap_format_6 *) -1; } -#pragma GCC diagnostic pop - /* Set the data pointer and swap everything. */ format6->glyph_index_array = (uint16_t *) (format6 + 1); for (i = 0; i < format6->entry_count; ++i) @@ -565,9 +549,6 @@ sfnt_read_cmap_format_8 (int fd, format8->reserved = header->length; format8->length = length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the fixed length data. */ min_size -= offsetof (struct sfnt_cmap_format_8, language); rc = read (fd, &format8->language, min_size); @@ -613,8 +594,6 @@ sfnt_read_cmap_format_8 (int fd, return (struct sfnt_cmap_format_8 *) -1; } -#pragma GCC diagnostic pop - /* Set the pointer to the variable length data. */ format8->groups = (struct sfnt_cmap_format_8_or_12_group *) (format8 + 1); @@ -662,9 +641,6 @@ sfnt_read_cmap_format_12 (int fd, format12->reserved = header->length; format12->length = length; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" - /* Read the fixed length data. */ min_size -= offsetof (struct sfnt_cmap_format_12, language); rc = read (fd, &format12->language, min_size); @@ -674,8 +650,6 @@ sfnt_read_cmap_format_12 (int fd, return (struct sfnt_cmap_format_12 *) -1; } -#pragma GCC diagnostic pop - /* Swap what was read. */ sfnt_swap32 (&format12->language); sfnt_swap32 (&format12->num_groups); @@ -991,31 +965,15 @@ sfnt_compare_uint16 (const void *a, const void *b) return ((int) *((uint16_t *) a)) - ((int) *((uint16_t *) b)); } -/* Look up the glyph corresponding to CHARACTER in the format 4 cmap - FORMAT4. Return 0 if no glyph was found. */ +/* Look up the glyph corresponding to CODE in the format 4 cmap + FORMAT4, using the table segment SEGMENT. Value is 0 if no glyph + was found. */ static sfnt_glyph -sfnt_lookup_glyph_4 (sfnt_char character, - struct sfnt_cmap_format_4 *format4) +sfnt_lookup_glyph_4_1 (uint16_t code, uint16_t segment, + struct sfnt_cmap_format_4 *format4) { - uint16_t *segment_address, *index; - uint16_t code, segment; - - if (character > 65535) - return 0; - - code = character; - - /* Find the segment above CHARACTER. */ - segment_address = sfnt_bsearch_above (&code, format4->end_code, - format4->seg_count_x2 / 2, - sizeof code, - sfnt_compare_uint16); - segment = segment_address - format4->end_code; - - /* If the segment starts too late, return 0. */ - if (!segment_address || format4->start_code[segment] > character) - return 0; + uint16_t *index; if (format4->id_range_offset[segment]) { @@ -1040,6 +998,63 @@ sfnt_lookup_glyph_4 (sfnt_char character, return (format4->id_delta[segment] + code) % 65536; } +/* Look up the glyph corresponding to CHARACTER in the format 4 cmap + FORMAT4. Return 0 if no glyph was found. */ + +static sfnt_glyph +sfnt_lookup_glyph_4 (sfnt_char character, + struct sfnt_cmap_format_4 *format4) +{ + uint16_t *segment_address; + uint16_t code, segment; + sfnt_glyph glyph; + + if (character > 65535) + return 0; + + code = character; + + /* Find the segment ending above or at CHARACTER. */ + segment_address = sfnt_bsearch_above (&code, format4->end_code, + format4->seg_count_x2 / 2, + sizeof code, + sfnt_compare_uint16); + segment = segment_address - format4->end_code; + + /* If the segment starts too late, return 0. */ + if (!segment_address || format4->start_code[segment] > character) + return 0; + + glyph = sfnt_lookup_glyph_4_1 (character, segment, format4); + + if (glyph) + return glyph; + + /* Droid Sans Mono has overlapping segments in its format 4 cmap + subtable where the first segment's end code is 32, while the + second segment's start code is also 32. The TrueType Reference + Manual says that mapping should begin by searching for the first + segment whose end code is greater than or equal to the character + being indexed, but that results in the first subtable being + found, which doesn't work, while the second table does. Try to + detect this situation and use the second table if possible. */ + + if (!glyph + /* The character being looked up is the current segment's end + code. */ + && code == format4->end_code[segment] + /* There is an additional segment. */ + && segment + 1 < format4->seg_count_x2 / 2 + /* That segment's start code is the same as this segment's end + code. */ + && format4->start_code[segment + 1] == format4->end_code[segment]) + /* Try the second segment. */ + return sfnt_lookup_glyph_4_1 (character, segment + 1, format4); + + /* Fail. */ + return 0; +} + /* Look up the glyph corresponding to CHARACTER in the format 6 cmap FORMAT6. Return 0 if no glyph was found. */ @@ -1107,7 +1122,7 @@ sfnt_lookup_glyph_12 (sfnt_char character, which must be in the correct encoding for the cmap table pointed to by DATA. */ -static sfnt_glyph +TEST_STATIC sfnt_glyph sfnt_lookup_glyph (sfnt_char character, struct sfnt_cmap_encoding_subtable_data *data) { @@ -1520,7 +1535,7 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, /* Calculate the minimum size of the glyph data. This is the size of the instruction length field followed by - glyf->number_of_contours * sizeof (uint16_t). */ + glyph->number_of_contours * sizeof (uint16_t). */ min_size = (glyph->number_of_contours * sizeof (uint16_t) + sizeof (uint16_t)); @@ -2023,26 +2038,51 @@ sfnt_read_glyph (sfnt_glyph glyph_code, struct sfnt_loca_table_long *loca_long) { struct sfnt_glyph glyph, *memory; - ptrdiff_t offset; + ptrdiff_t offset, next_offset; + + /* Check the glyph code is within bounds. */ + if (glyph_code > 65535) + return NULL; if (loca_short) { - /* Check that the glyph is within bounds. */ - if (glyph_code > loca_short->num_offsets) + /* Check that the glyph is within bounds. glyph_code + 1 is the + entry in the table which defines the length of the glyph. */ + if (glyph_code + 1 >= loca_short->num_offsets) return NULL; offset = loca_short->offsets[glyph_code] * 2; + next_offset = loca_short->offsets[glyph_code + 1] * 2; } else if (loca_long) { - if (glyph_code > loca_long->num_offsets) + if (glyph_code + 1 >= loca_long->num_offsets) return NULL; offset = loca_long->offsets[glyph_code]; + next_offset = loca_long->offsets[glyph_code + 1]; } else abort (); + /* If offset - next_offset is 0, then the glyph is empty. Its + horizontal advance may still be provided by the hmtx table. */ + + if (offset == next_offset) + { + glyph.number_of_contours = 0; + glyph.xmin = 0; + glyph.ymin = 0; + glyph.xmax = 0; + glyph.ymax = 0; + glyph.simple = xmalloc (sizeof *glyph.simple); + glyph.compound = NULL; + memset (glyph.simple, 0, sizeof *glyph.simple); + memory = xmalloc (sizeof *memory); + *memory = glyph; + return memory; + } + /* Verify that GLYF is big enough to hold a glyph at OFFSET. */ if (glyf->size < offset + SFNT_ENDOF (struct sfnt_glyph, ymax, sfnt_fword)) @@ -2102,7 +2142,7 @@ sfnt_read_glyph (sfnt_glyph glyph_code, /* Free a glyph returned from sfnt_read_glyph. GLYPH may be NULL. */ -static void +TEST_STATIC void sfnt_free_glyph (struct sfnt_glyph *glyph) { if (!glyph) @@ -2122,7 +2162,7 @@ sfnt_free_glyph (struct sfnt_glyph *glyph) static void sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component, - sfnt_fixed *x, sfnt_fixed *y, + sfnt_fixed *restrict x, sfnt_fixed *restrict y, size_t num_coordinates) { double m1, m2, m3; @@ -2516,8 +2556,8 @@ static void sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2, struct sfnt_point *result) { - result->x = (control1->x + control2->x) / 2; - result->y = (control1->y + control2->y) / 2; + result->x = control1->x + ((control2->x - control1->x) >> 1); + result->y = control1->y + ((control2->y - control1->y) >> 1); } /* Decompose GLYPH into its individual components. Call MOVE_TO to @@ -2548,7 +2588,7 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, sfnt_free_glyph_proc free_glyph, void *dcontext) { - size_t here, last; + size_t here, start, last; struct sfnt_point pen, control1, control2; struct sfnt_compound_glyph_context context; size_t n; @@ -2556,8 +2596,8 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, if (glyph->simple) { if (!glyph->number_of_contours) - /* No contours. */ - return 1; + /* No contours. Nothing needs to be decomposed. */ + return 0; here = 0; @@ -2573,6 +2613,9 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, pen.y = glyph->simple->y_coordinates[here] << 16U; move_to (pen, dcontext); + /* Record start so the contour can be closed. */ + start = here; + /* If there is only one point in a contour, draw a one pixel wide line. */ if (last == here) @@ -2635,6 +2678,39 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, curve_to (control1, pen, dcontext); } } + + /* Now close the contour if there is more than one point + inside it. */ + if (start != here - 1) + { + /* Restore here after the for loop increased it. */ + here --; + + if (glyph->simple->flags[start] & 01) /* On Curve */ + { + pen.x = glyph->simple->x_coordinates[start] << 16U; + pen.y = glyph->simple->y_coordinates[start] << 16U; + + /* See if the last point (in this case, `here') was + on the curve. If it wasn't, then curve from + there to here. */ + if (!(glyph->simple->flags[here] & 01)) + { + control1.x + = glyph->simple->x_coordinates[here] << 16U; + control1.y + = glyph->simple->y_coordinates[here] << 16U; + curve_to (control1, pen, dcontext); + } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); + } + + /* Restore here to where it was earlier. */ + here++; + } } return 0; @@ -2659,7 +2735,7 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, if (!context.num_end_points) /* No contours. */ - goto fail; + goto early; here = 0; @@ -2675,6 +2751,9 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, pen.y = context.y_coordinates[here]; move_to (pen, dcontext); + /* Record start so the contour can be closed. */ + start = here; + /* If there is only one point in a contour, draw a one pixel wide line. */ if (last == here) @@ -2735,8 +2814,40 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, curve_to (control1, pen, dcontext); } } + + /* Now close the contour if there is more than one point + inside it. */ + if (start != here - 1) + { + /* Restore here after the for loop increased it. */ + here --; + + if (context.flags[start] & 01) /* On Curve */ + { + pen.x = context.x_coordinates[start]; + pen.y = context.y_coordinates[start]; + + /* See if the last point (in this case, `here') was + on the curve. If it wasn't, then curve from + there to here. */ + if (!(context.flags[here] & 01)) + { + control1.x = context.x_coordinates[here]; + control1.y = context.y_coordinates[here]; + curve_to (control1, pen, dcontext); + } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); + } + + /* Restore here to where it was earlier. */ + here++; + } } + early: xfree (context.x_coordinates); xfree (context.y_coordinates); xfree (context.flags); @@ -2751,6 +2862,25 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, return 1; } +struct sfnt_build_glyph_outline_context +{ + /* The outline being built. */ + struct sfnt_glyph_outline *outline; + + /* The head table. */ + struct sfnt_head_table *head; + + /* The pixel size being used, and any extra flags to apply to the + outline at this point. */ + int pixel_size; + + /* Factor to multiply positions by to get the pixel width. */ + sfnt_fixed factor; + + /* The position of the pen in 16.16 fixed point format. */ + sfnt_fixed x, y; +}; + /* Global state for sfnt_build_glyph_outline and related functions. */ static struct sfnt_build_glyph_outline_context build_outline_context; @@ -2794,7 +2924,7 @@ sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y) /* Extend outline bounding box. */ - if (outline->outline_used == 3) + if (outline->outline_used == 1) { /* These are the first points in the outline. */ outline->xmin = outline->xmax = x; @@ -2811,40 +2941,6 @@ sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y) return outline; } -/* Set the pen size to the specified point and return. POINT will be - scaled up to the pixel size. */ - -static void -sfnt_move_to_and_build (struct sfnt_point point, void *dcontext) -{ - sfnt_fixed x, y; - - x = build_outline_context.factor * point.x; - y = build_outline_context.factor * point.y; - - build_outline_context.outline = sfnt_build_append (0, x, y); - build_outline_context.x = x; - build_outline_context.y = y; -} - -/* Record a line to the specified point and return. POINT will be - scaled up to the pixel size. */ - -static void -sfnt_line_to_and_build (struct sfnt_point point, void *dcontext) -{ - sfnt_fixed x, y; - - x = build_outline_context.factor * point.x; - y = build_outline_context.factor * point.y; - - build_outline_context.outline - = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO, - x, y); - build_outline_context.x = x; - build_outline_context.y = y; -} - /* Multiply the two 16.16 fixed point numbers X and Y. Return the result regardless of overflow. */ @@ -2878,6 +2974,40 @@ sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y) #endif } +/* Set the pen size to the specified point and return. POINT will be + scaled up to the pixel size. */ + +static void +sfnt_move_to_and_build (struct sfnt_point point, void *dcontext) +{ + sfnt_fixed x, y; + + x = sfnt_mul_fixed (build_outline_context.factor, point.x); + y = sfnt_mul_fixed (build_outline_context.factor, point.y); + + build_outline_context.outline = sfnt_build_append (0, x, y); + build_outline_context.x = x; + build_outline_context.y = y; +} + +/* Record a line to the specified point and return. POINT will be + scaled up to the pixel size. */ + +static void +sfnt_line_to_and_build (struct sfnt_point point, void *dcontext) +{ + sfnt_fixed x, y; + + x = sfnt_mul_fixed (build_outline_context.factor, point.x); + y = sfnt_mul_fixed (build_outline_context.factor, point.y); + + build_outline_context.outline + = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO, + x, y); + build_outline_context.x = x; + build_outline_context.y = y; +} + /* Divide the two 16.16 fixed point numbers X and Y. Return the result regardless of overflow. */ @@ -2957,7 +3087,7 @@ sfnt_curve_to_and_build_1 (struct sfnt_point control0, struct sfnt_point control1, struct sfnt_point endpoint) { - struct sfnt_point ab; + struct sfnt_point ab, bc, abbc; /* control0, control and endpoint make up the spline. Figure out its distance from a line. */ @@ -2972,15 +3102,17 @@ sfnt_curve_to_and_build_1 (struct sfnt_point control0, } else { - /* Split the spline between control0 and control1. + /* Calculate new control points. Maybe apply a recursion limit here? */ sfnt_lerp_half (&control0, &control1, &ab); + sfnt_lerp_half (&control1, &endpoint, &bc); + sfnt_lerp_half (&ab, &bc, &abbc); /* Keep splitting until a flat enough spline results. */ - sfnt_curve_to_and_build_1 (control0, ab, control1); + sfnt_curve_to_and_build_1 (control0, ab, abbc); /* Then go on with the spline between control1 and endpoint. */ - sfnt_curve_to_and_build_1 (ab, control1, endpoint); + sfnt_curve_to_and_build_1 (abbc, bc, endpoint); } } @@ -2997,17 +3129,21 @@ sfnt_curve_to_and_build (struct sfnt_point control, control0.x = build_outline_context.x; control0.y = build_outline_context.y; - control.x *= build_outline_context.factor; - control.y *= build_outline_context.factor; - endpoint.x *= build_outline_context.factor; - endpoint.y *= build_outline_context.factor; + control.x = sfnt_mul_fixed (control.x, + build_outline_context.factor); + control.y = sfnt_mul_fixed (control.y, + build_outline_context.factor); + endpoint.x = sfnt_mul_fixed (endpoint.x, + build_outline_context.factor); + endpoint.y = sfnt_mul_fixed (endpoint.y, + build_outline_context.factor); sfnt_curve_to_and_build_1 (control0, control, endpoint); } /* Non-reentrantly build the outline for the specified GLYPH at the - given pixel size. Return the outline data upon success, or NULL - upon failure. + given pixel size. Return the outline data with a refcount of 0 + upon success, or NULL upon failure. Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain glyphs for compound glyph subcomponents. @@ -3029,6 +3165,7 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline)); outline->outline_size = 40; outline->outline_used = 0; + outline->refcount = 0; outline->outline = (struct sfnt_glyph_outline_command *) (outline + 1); @@ -3059,7 +3196,8 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, It would be nice to get rid of this floating point arithmetic at some point. */ build_outline_context.factor - = (double) pixel_size / head->units_per_em; + = sfnt_div_fixed (pixel_size << 16, + head->units_per_em << 16); /* Decompose the outline. */ rc = sfnt_decompose_glyph (glyph, sfnt_move_to_and_build, @@ -3108,11 +3246,17 @@ sfnt_poly_grid_ceil (sfnt_fixed f) & ~(SFNT_POLY_STEP - 1)) + SFNT_POLY_START); } +enum + { + SFNT_POLY_ALIGNMENT = 4, + }; + /* Initialize the specified RASTER in preparation for displaying spans - for OUTLINE. The caller must then set RASTER->cells to a zeroed - array of size RASTER->width * RASTER->height. */ + for OUTLINE, and set RASTER->refcount to 0. The caller must then + set RASTER->cells to a zeroed array of size RASTER->stride * + RASTER->height, aligned to RASTER. */ -static void +TEST_STATIC void sfnt_prepare_raster (struct sfnt_raster *raster, struct sfnt_glyph_outline *outline) { @@ -3120,10 +3264,17 @@ sfnt_prepare_raster (struct sfnt_raster *raster, = sfnt_ceil_fixed (outline->xmax - outline->xmin) >> 16; raster->height = sfnt_ceil_fixed (outline->ymax - outline->ymin) >> 16; + raster->refcount = 0; + + /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */ + raster->stride = ((raster->width + + (SFNT_POLY_ALIGNMENT - 1)) + & ~(SFNT_POLY_ALIGNMENT - 1)); + raster->offx - = sfnt_floor_fixed (outline->xmin); + = sfnt_floor_fixed (outline->xmin) >> 16; raster->offy - = sfnt_floor_fixed (raster->height - outline->ymax); + = sfnt_floor_fixed (outline->ymin) >> 16; } typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t, @@ -3131,13 +3282,13 @@ typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t, typedef void (*sfnt_span_proc) (struct sfnt_edge *, sfnt_fixed, void *); /* Move EDGE->x forward, assuming that the scanline has moved upwards - by DY. */ + by SFNT_POLY_STEP. */ static void -sfnt_step_edge_by (struct sfnt_edge *edge, sfnt_fixed dy) +sfnt_step_edge (struct sfnt_edge *edge) { /* Step edge. */ - edge->x += sfnt_mul_fixed (edge->step_x, dy); + edge->x += edge->step_x; } /* Build a list of edges for each contour in OUTLINE, applying @@ -3151,31 +3302,26 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, sfnt_edge_proc edge_proc, void *dcontext) { struct sfnt_edge *edges; - size_t i, edge, start, next_vertex, y; - sfnt_fixed dx, dy, bot; + size_t i, edge, next_vertex; + sfnt_fixed dx, dy, bot, step_x; int inc_x; - size_t top, bottom; + size_t top, bottom, y; edges = alloca (outline->outline_used * sizeof *edges); edge = 0; - /* First outline currently being processed. */ - start = 0; - - for (i = 0; i < outline->outline_used; i++) + for (i = 0; i < outline->outline_used; ++i) { - if (!(outline->outline[i].flags & SFNT_GLYPH_OUTLINE_LINETO)) - /* Flush the edge. */ - start = i; - /* Set NEXT_VERTEX to the next point (vertex) in this contour. - If i + 3 is the end of the contour, then the next point is - its start, so wrap it around to there. */ + + If i is past the end of the contour, then don't build edges + for this point. */ next_vertex = i + 1; + if (next_vertex == outline->outline_used || !(outline->outline[next_vertex].flags & SFNT_GLYPH_OUTLINE_LINETO)) - next_vertex = start; + continue; /* Skip past horizontal vertices. */ if (outline->outline[next_vertex].y == outline->outline[i].y) @@ -3220,6 +3366,10 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, dy = abs (outline->outline[top].y - outline->outline[bottom].y); +#ifdef TEST + edges[edge].source_x = edges[edge].x; +#endif + /* Compute the increment. This is which direction X moves in for each increase in Y. */ @@ -3233,14 +3383,24 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, /* Compute the step X. This is how much X changes for each increase in Y. */ - edges[edge].step_x = inc_x * sfnt_div_fixed (dx, dy); + step_x = inc_x * sfnt_div_fixed (dx, dy); /* Step to first grid point. */ y = sfnt_poly_grid_ceil (bot); - sfnt_step_edge_by (&edges[edge], bot - y); + + /* If rounding would make the edge not cover any area, skip this + edge. */ + if (y > edges[edge].top) + continue; + + edges[edge].x += sfnt_mul_fixed (step_x, bot - y); edges[edge].bottom = y; edges[edge].next = NULL; + /* Compute the step X scaled to the poly step. */ + edges[edge].step_x + = sfnt_mul_fixed (step_x, SFNT_POLY_STEP); + edge++; } @@ -3344,7 +3504,7 @@ sfnt_poly_edges (struct sfnt_edge *edges, size_t size, /* Step all edges. */ for (a = active; a; a = a->next) - sfnt_step_edge_by (a, SFNT_POLY_STEP); + sfnt_step_edge (a); /* Resort on X axis. */ for (prev = &active; (a = *prev) && (n = a->next);) @@ -3380,7 +3540,7 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, { unsigned char *start; unsigned char *coverage; - sfnt_fixed left, right; + sfnt_fixed left, right, end; unsigned short w, a; int row, col; @@ -3408,7 +3568,13 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, right are now 16.2. */ left = sfnt_poly_grid_ceil (x0) >> (16 - SFNT_POLY_SHIFT); right = sfnt_poly_grid_ceil (x1) >> (16 - SFNT_POLY_SHIFT); - start = raster->cells + row * raster->width; +#if 7 > __GNUC__ + start = raster->cells + row * raster->stride; +#else + start = __builtin_assume_aligned ((raster->cells + + row * raster->stride), + SFNT_POLY_ALIGNMENT); +#endif start += left >> SFNT_POLY_SHIFT; /* Compute coverage for first pixel, then poly. */ @@ -3420,7 +3586,10 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, map, unlike row which indexes the raster. */ col = 0; - while (left < right && (left & SFNT_POLY_MASK)) + /* Precompute this to allow for better optimizations. */ + end = ((left + SFNT_POLY_SAMPLE - 1) & ~SFNT_POLY_MASK); + + while (left < right && left < end) left++, w += coverage[col++]; a = *start + w; @@ -3477,10 +3646,6 @@ sfnt_poly_span (struct sfnt_edge *start, sfnt_fixed y, for (edge = start; edge; edge = edge->next) { - /* Skip out of bounds spans. This ought to go away. */ - if (!(y >= edge->bottom && y < edge->top)) - continue; - if (!winding) x0 = edge->x; else @@ -3528,10 +3693,10 @@ sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline) sfnt_prepare_raster (&raster, outline); /* Allocate the raster data. */ - data = xmalloc (sizeof *data + raster.width * raster.height); + data = xmalloc (sizeof *data + raster.stride * raster.height); *data = raster; data->cells = (unsigned char *) (data + 1); - memset (data->cells, 0, raster.width * raster.height); + memset (data->cells, 0, raster.stride * raster.height); /* Generate edges for the outline, polying each array of edges to the raster. */ @@ -4131,16 +4296,25 @@ sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges, for (i = 0; i < num_edges; ++i) { - printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n", + printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n" + "/* edge step_x: %g, source_x: %g (%d) */\n", sfnt_coerce_fixed (edges[i].x), sfnt_coerce_fixed (edges[i].top), sfnt_coerce_fixed (edges[i].bottom), - edges[i].winding); + edges[i].winding, + sfnt_coerce_fixed (edges[i].step_x), + sfnt_coerce_fixed (edges[i].source_x), + edges[i].source_x); #ifdef TEST_VERTEX printf ("ctx.fillRect (%g, %g, 1, 1);\n", sfnt_coerce_fixed (edges[i].x), sfnt_coerce_fixed (sfnt_test_max - edges[i].y)); +#else + printf ("ctx.fillRect (%g, %g, 1, 1);\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (sfnt_test_max + - edges[i].bottom)); #endif } @@ -4157,7 +4331,7 @@ sfnt_test_raster (struct sfnt_raster *raster) for (y = 0; y < raster->height; ++y) { for (x = 0; x < raster->width; ++x) - printf ("%3d ", (int) raster->cells[y * raster->width + x]); + printf ("%3d ", (int) raster->cells[y * raster->stride + x]); puts (""); } } @@ -4411,7 +4585,7 @@ main (int argc, char **argv) /* Time this important bit. */ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); outline = sfnt_build_glyph_outline (glyph, head, - 40, + 45, sfnt_test_get_glyph, sfnt_test_free_glyph, &dcontext); @@ -4449,13 +4623,23 @@ main (int argc, char **argv) sfnt_build_outline_edges (outline, sfnt_test_edge, NULL); + raster = NULL; + clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); - raster = sfnt_raster_glyph_outline (outline); + + for (i = 0; i < 400; ++i) + { + xfree (raster); + raster = sfnt_raster_glyph_outline (outline); + } + clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end); sub2 = timespec_sub (end, start); /* Print out the raster. */ sfnt_test_raster (raster); + printf ("raster offsets: %d, %d\n", + raster->offx, raster->offy); xfree (raster); @@ -4468,7 +4652,7 @@ main (int argc, char **argv) if (hmtx && head) { - if (!sfnt_lookup_glyph_metrics (code, 40, + if (!sfnt_lookup_glyph_metrics (code, 36, &metrics, hmtx, hhea, head, maxp)) @@ -4482,7 +4666,7 @@ main (int argc, char **argv) printf ("time spent building edges: %lld sec %ld nsec\n", (long long) sub1.tv_sec, sub1.tv_nsec); printf ("time spent rasterizing: %lld sec %ld nsec\n", - (long long) sub2.tv_sec, sub2.tv_nsec); + (long long) sub2.tv_sec / 400, sub2.tv_nsec / 400); xfree (outline); } diff --git a/src/sfnt.h b/src/sfnt.h index 868d0f9b5a9..91d7b261cb0 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -663,6 +663,9 @@ struct sfnt_glyph_outline /* Rectangle defining bounds of the outline. Namely, the minimum and maximum X and Y positions. */ sfnt_fixed xmin, ymin, xmax, ymax; + + /* Reference count. Initially zero. */ + short refcount; }; enum sfnt_glyph_outline_flags @@ -670,39 +673,26 @@ enum sfnt_glyph_outline_flags SFNT_GLYPH_OUTLINE_LINETO = (1 << 1), }; -struct sfnt_build_glyph_outline_context -{ - /* The outline being built. */ - struct sfnt_glyph_outline *outline; - - /* The head table. */ - struct sfnt_head_table *head; - - /* The pixel size being used, and any extra flags to apply to the - outline at this point. */ - int pixel_size; - - /* Factor to multiply positions by to get the pixel width. */ - double factor; - - /* The position of the pen in 16.16 fixed point format. */ - sfnt_fixed x, y; -}; - /* Glyph rasterization. */ struct sfnt_raster { + /* Pointer to coverage data. */ + unsigned char *cells; + /* Basic dimensions of the raster. */ unsigned short width, height; /* Integer offset to apply to positions in the raster. */ - unsigned short offx, offy; + short offx, offy; - /* Pointer to coverage data. */ - unsigned char *cells; + /* The raster stride. */ + unsigned short stride; + + /* Reference count. Initially zero. */ + unsigned short refcount; }; struct sfnt_edge @@ -716,8 +706,15 @@ struct sfnt_edge /* X position, top and bottom of edges. */ sfnt_fixed x, top, bottom; - /* step_x is how many pixels to move for each increase in Y. */ + /* step_x is how many pixels to move for each increase in Y by + SFNT_POLY_STEP. */ sfnt_fixed step_x; + +#ifdef TEST + /* Value of x before initial adjustment of bottom to match the + grid. */ + sfnt_fixed source_x; +#endif }; @@ -878,6 +875,12 @@ enum sfnt_meta_data_tag +#define SFNT_CEIL_FIXED(fixed) \ + (!((fixed) & 0177777) ? (fixed) \ + : ((fixed) + 0200000) & 037777600000) + + + /* Function declarations. Keep these sorted by the order in which they appear in sfnt.c. Keep each line no longer than 80 columns. */ @@ -890,9 +893,12 @@ extern struct sfnt_offset_subtable *sfnt_read_table_directory (int); int, struct sfnt_offset_subtable *, \ struct sfnt_cmap_encoding_subtable **, \ struct sfnt_cmap_encoding_subtable_data *** -static struct sfnt_cmap_table *sfnt_read_cmap_table (PROTOTYPE); +extern struct sfnt_cmap_table *sfnt_read_cmap_table (PROTOTYPE); #undef PROTOTYPE +extern sfnt_glyph sfnt_lookup_glyph (sfnt_char, + struct sfnt_cmap_encoding_subtable_data *); + #define PROTOTYPE int, struct sfnt_offset_subtable * extern struct sfnt_head_table *sfnt_read_head_table (PROTOTYPE); extern struct sfnt_hhea_table *sfnt_read_hhea_table (PROTOTYPE); @@ -905,6 +911,7 @@ extern struct sfnt_glyf_table *sfnt_read_glyf_table (PROTOTYPE); extern struct sfnt_glyph *sfnt_read_glyph (sfnt_glyph, struct sfnt_glyf_table *, struct sfnt_loca_table_short *, struct sfnt_loca_table_long *); +extern void sfnt_free_glyph (struct sfnt_glyph *); #define PROTOTYPE \ struct sfnt_glyph *, \ diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index d3c05fa4ac7..47aa27dc113 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -19,18 +19,505 @@ along with GNU Emacs. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include +#include +#include + +#include #include "androidterm.h" #include "sfntfont.h" +#include "pdumper.h" +#include "blockinput.h" +#include "android.h" + +/* Array of directories to search for system fonts. */ +const char *system_font_directories[] = + { + "/system/fonts", + }; + +/* The font cache. */ +static Lisp_Object font_cache; + + + +static unsigned int +sfntfont_android_saturate32 (unsigned int a, unsigned int b) +{ + unsigned int c; + + c = a + b; + + if (c < a) + c = -1; + + return c; +} + +/* Scale each of the four packed bytes in P in the low 16 bits of P by + SCALE. Return the result. + + SCALE is an integer between 0 and 256. */ + +static unsigned int +sfntfont_android_scale32 (unsigned int scale, unsigned int p) +{ + uint32_t ag, rb; + uint32_t scaled_ag, scaled_rb; + + ag = (p & 0xFF00FF00) >> 8; + rb = (p & 0x00FF00FF); + + scaled_ag = (scale * ag) & 0xFF00FF00; + scaled_rb = (scale * rb) >> 8 & 0x00FF00FF; + + return scaled_ag | scaled_rb; +} + +static unsigned int +sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32) +{ + unsigned int i; + + b32 &= 0xff00ff; + i = a8 * b32 + 0x800080; + + return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff; +} + +/* Blend two pixels SRC and DST without utilizing any control flow. + SRC must be in premultiplied ARGB8888 format, and DST must be in + premultiplied ABGR8888 format. Value is in premultiplied ABGR8888 + format. */ + +static unsigned int +sfntfont_android_blend (unsigned int src, unsigned int dst) +{ + unsigned int a, br_part, ag_part, src_rb, both; + + a = (src >> 24); + br_part = sfntfont_android_mul8x2 (255 - a, dst); + ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8; + + both = ag_part | br_part; + + /* Swizzle src. */ + src_rb = src & 0x00ff00ff; + src = src & ~0x00ff00ff; + src |= (src_rb >> 16 | src_rb << 16); + + /* Saturating is unnecessary but helps find bugs. */ + return sfntfont_android_saturate32 (both, src); +} + +#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7)) + +/* Blend two pixels SRC and DST without utilizing any control flow. + Both SRC and DST are expected to be in premultiplied ARGB8888 + format. Value is returned in premultiplied ARGB8888 format. */ + +static unsigned int +sfntfont_android_blendrgb (unsigned int src, unsigned int dst) +{ + unsigned int a, rb_part, ag_part, both; + + a = (src >> 24); + rb_part = sfntfont_android_mul8x2 (255 - a, dst); + ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8; + + both = ag_part | rb_part; + + /* Saturating is unnecessary but helps find bugs. */ + return sfntfont_android_saturate32 (both, src); +} + +/* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE + onto the native-endian ABGR8888 bitmap described by DEST and + BITMAP_INFO. RECT is the subset of the bitmap to composite. */ + +static void +sfntfont_android_composite_bitmap (unsigned char *restrict buffer, + size_t stride, + unsigned char *restrict dest, + AndroidBitmapInfo *bitmap_info, + struct android_rectangle *text_rectangle, + struct android_rectangle *rect) +{ + unsigned int *src_row; + unsigned int *dst_row; + unsigned int i, src_y, x, src_x, max_x, dst_x; + + if ((intptr_t) dest & 3 || bitmap_info->stride & 3) + /* This shouldn't be possible as Android is supposed to align the + bitmap to at least a 4 byte boundary. */ + emacs_abort (); + else + { + for (i = 0; i < rect->height; ++i) + { + if (i + rect->y >= bitmap_info->height) + /* Done. */ + return; + + src_y = i + (rect->y - text_rectangle->y); + + src_row = (unsigned int *) ((buffer + src_y * stride)); + dst_row = (unsigned int *) (dest + ((i + rect->y) + * bitmap_info->stride)); + + /* Figure out where the loop below should end. */ + max_x = min (rect->width, bitmap_info->width - rect->x); + + /* Keep this loop simple! */ + for (x = 0; x < max_x; ++x) + { + src_x = x + (rect->x - text_rectangle->x); + dst_x = x + rect->x; + + dst_row[dst_x] + = sfntfont_android_blend (src_row[src_x], + dst_row[dst_x]); + } + } + } +} + +/* Calculate the union containing both A and B, both boxes. Place the + result in RESULT. */ + +static void +sfntfont_android_union_boxes (struct gui_box a, struct gui_box b, + struct gui_box *result) +{ + result->x1 = min (a.x1, b.x1); + result->y1 = min (a.y1, b.y1); + result->x2 = max (a.x2, b.x2); + result->y2 = max (a.y2, b.y2); +} + +/* Draw the specified glyph rasters from FROM to TO on behalf of S, + using S->gc. Fill the background if WITH_BACKGROUND is true. + + See init_sfntfont_vendor and sfntfont_draw for more details. */ + +static void +sfntfont_android_put_glyphs (struct glyph_string *s, int from, + int to, int x, int y, bool with_background, + struct sfnt_raster **rasters, + int *x_coords) +{ + struct android_rectangle background, text_rectangle, rect; + struct gui_box text, character; + unsigned int *buffer, *row; + unsigned char *restrict raster_row; + size_t stride, i; + AndroidBitmapInfo bitmap_info; + unsigned char *bitmap_data; + jobject bitmap; + int left, top, temp_y; + unsigned int prod, raster_y; + + if (!s->gc->num_clip_rects) + /* Clip region is empty. */ + return; + + if (from == to) + /* Nothing to draw. */ + return; + + USE_SAFE_ALLOCA; + + prepare_face_for_display (s->f, s->face); + + /* Build the scanline buffer. Figure out the bounds of the + background. */ + memset (&background, 0, sizeof background); + + if (with_background) + { + background.x = x; + background.y = y - FONT_BASE (s->font); + background.width = s->width; + background.height = FONT_HEIGHT (s->font); + } + + /* Now figure out the bounds of the text. */ + + if (rasters[0]) + { + text.x1 = x_coords[0] + rasters[0]->offx; + text.x2 = text.x1 + rasters[0]->width; + text.y1 = y - (rasters[0]->height + rasters[0]->offy); + text.y2 = y - rasters[0]->offy; + } + else + memset (&text, 0, sizeof text); + + for (i = 1; i < to - from; ++i) + { + /* See if text has to be extended. */ + + if (!rasters[i]) + continue; + + character.x1 = x_coords[i] + rasters[i]->offx; + character.x2 = character.x1 + rasters[i]->width; + character.y1 = y - (rasters[i]->height + rasters[i]->offy); + character.y2 = y - rasters[i]->offy; + + sfntfont_android_union_boxes (text, character, &text); + } + + /* Union the background rect with the text rectangle. */ + text_rectangle.x = text.x1; + text_rectangle.y = text.y1; + text_rectangle.width = text.x2 - text.x1; + text_rectangle.height = text.y2 - text.y1; + gui_union_rectangles (&background, &text_rectangle, + &text_rectangle); + + /* Allocate enough to hold text_rectangle.height, aligned to 8 + bytes. Then fill it with the background. */ + stride = (text_rectangle.width * sizeof *buffer) + 7 & ~7; + SAFE_NALLOCA (buffer, text_rectangle.height, stride); + memset (buffer, 0, text_rectangle.height * stride); + + if (with_background) + { + /* Fill the background. First, offset the background rectangle + to become relative from text_rectangle.x, + text_rectangle.y. */ + background.x = background.x - text_rectangle.x; + background.y = background.y - text_rectangle.y; + eassert (background.x >= 0 && background.y >= 0); + + for (temp_y = background.y; (temp_y + < (background.y + + background.height)); + ++temp_y) + { + row = (unsigned int *) ((unsigned char *) buffer + + stride * temp_y); + + for (x = background.x; x < background.x + background.width; ++x) + row[x] = s->gc->background | 0xff000000; + } + } + + /* Draw all the rasters onto the buffer. */ + for (i = 0; i < to - from; ++i) + { + if (!rasters[i]) + continue; + + /* Figure out the top and left of the raster relative to + text_rectangle. */ + left = x_coords[i] + rasters[i]->offx - text_rectangle.x; + + /* Note that negative offy represents the part of the text that + lies below the baseline. */ + top = (y - (rasters[i]->height + rasters[i]->offy) + - text_rectangle.y); + eassert (left >= 0 && top >= 0); + + /* Draw the raster onto the temporary bitmap using the + foreground color scaled by the alpha map. */ + + for (raster_y = 0; raster_y < rasters[i]->height; ++raster_y) + { + row = (unsigned int *) ((unsigned char *) buffer + + stride * (raster_y + top)); + raster_row = &rasters[i]->cells[raster_y * rasters[i]->stride]; + + for (x = 0; x < rasters[i]->width; ++x) + { + prod + = sfntfont_android_scale32 (U255TO256 (raster_row[x]), + (s->gc->foreground + | 0xff000000)); + row[left + x] + = sfntfont_android_blendrgb (prod, row[left + x]); + } + } + } + + /* Lock the bitmap. It must be unlocked later. */ + bitmap_data = android_lock_bitmap (FRAME_ANDROID_WINDOW (s->f), + &bitmap_info, &bitmap); + + /* If locking the bitmap fails, just discard the data that was + allocated. */ + if (!bitmap_data) + { + SAFE_FREE (); + return; + } + + /* Loop over each clip rect in the GC. */ + eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); + + if (s->gc->num_clip_rects > 0) + { + for (i = 0; i < s->gc->num_clip_rects; ++i) + { + if (!gui_intersect_rectangles (&s->gc->clip_rects[i], + &text_rectangle, &rect)) + /* Outside the clip region. */ + continue; + + /* Composite the intersection onto the buffer. */ + sfntfont_android_composite_bitmap ((unsigned char *) buffer, + stride, bitmap_data, + &bitmap_info, + &text_rectangle, &rect); + } + } + else /* gc->num_clip_rects < 0 */ + sfntfont_android_composite_bitmap ((unsigned char *) buffer, + stride, bitmap_data, + &bitmap_info, + &text_rectangle, + &text_rectangle); + + /* Release the bitmap. */ + AndroidBitmap_unlockPixels (android_java_env, bitmap); + ANDROID_DELETE_LOCAL_REF (bitmap); + + /* Damage the window by the text rectangle. */ + android_damage_window (FRAME_ANDROID_WINDOW (s->f), + &text_rectangle); + + /* Release the temporary scanline buffer. */ + SAFE_FREE (); +} + + + +/* Font driver definition. */ + +/* Return the font cache for this font driver. F is ignored. */ + +static Lisp_Object +sfntfont_android_get_cache (struct frame *f) +{ + return font_cache; +} + +/* The Android sfntfont driver. */ +const struct font_driver android_sfntfont_driver = + { + .type = LISPSYM_INITIALLY (Qsfnt_android), + .case_sensitive = true, + .get_cache = sfntfont_android_get_cache, + .list = sfntfont_list, + .match = sfntfont_match, + .draw = sfntfont_draw, + .open_font = sfntfont_open, + .close_font = sfntfont_close, + .encode_char = sfntfont_encode_char, + .text_extents = sfntfont_text_extents, + .list_family = sfntfont_list_family, + + /* TODO: list_family, shaping. */ + }; + + + +/* This is an ugly hack that should go away, but I can't think of + how. */ + +DEFUN ("android-enumerate-fonts", Fandroid_enumerate_fonts, + Sandroid_enumerate_fonts, 0, 0, 0, + doc: /* Enumerate fonts present on the system. + +Signal an error if fonts have already been enumerated. This would +normally have been done in C, but reading fonts require Lisp to be +loaded before character sets are made available. */) + (void) +{ + DIR *dir; + int i; + struct dirent *dirent; + char name[PATH_MAX * 2]; + static bool enumerated; + + if (enumerated) + error ("Fonts have already been enumerated"); + enumerated = true; + + block_input (); + + /* Scan through each of the system font directories. Enumerate each + font that looks like a TrueType font. */ + for (i = 0; i < ARRAYELTS (system_font_directories); ++i) + { + dir = opendir (system_font_directories[i]); + + if (!dir) + continue; + + while ((dirent = readdir (dir))) + { + /* If it contains (not ends with!) with .ttf, then enumerate + it. */ + + if (strstr (dirent->d_name, ".ttf")) + { + sprintf (name, "%s/%s", system_font_directories[i], + dirent->d_name); + sfnt_enum_font (name); + } + } + + closedir (dir); + } + + unblock_input (); + + return Qnil; +} + + + +static void +syms_of_sfntfont_android_for_pdumper (void) +{ + init_sfntfont_vendor (Qsfnt_android, &android_sfntfont_driver, + sfntfont_android_put_glyphs); + register_font_driver (&android_sfntfont_driver, NULL); +} void init_sfntfont_android (void) { - + /* Make sure to pick the right Sans Serif font depending on what + version of Android the device is running. */ + if (android_get_device_api_level () >= 15) + Vsfnt_default_family_alist + = list2 (Fcons (build_string ("Monospace"), + build_string ("Droid Sans Mono")), + Fcons (build_string ("Sans Serif"), + build_string ("Roboto"))); + else + Vsfnt_default_family_alist + = list2 (Fcons (build_string ("Monospace"), + build_string ("Droid Sans Mono")), + Fcons (build_string ("Sans Serif"), + build_string ("Droid Sans"))); } void syms_of_sfntfont_android (void) { + DEFSYM (Qsfnt_android, "sfnt-android"); + DEFSYM (Qandroid_enumerate_fonts, "android-enumerate-fonts"); + Fput (Qandroid, Qfont_driver_superseded_by, Qsfnt_android); + + font_cache = list (Qnil); + staticpro (&font_cache); + + defsubr (&Sandroid_enumerate_fonts); + pdumper_do_now_and_after_load (syms_of_sfntfont_android_for_pdumper); } diff --git a/src/sfntfont.c b/src/sfntfont.c index a9cc6d11b45..9206fbc6629 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -20,12 +20,25 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include + #include "lisp.h" -#include "sfnt.h" + +#include "blockinput.h" +#include "charset.h" #include "coding.h" +#include "font.h" +#include "frame.h" +#include "math.h" +#include "sfnt.h" +#include "sfntfont.h" + +/* For FRAME_FONT. */ +#include TERM_HEADER /* Generic font driver for sfnt-based fonts (currently TrueType, but - it would be easy to add CFF support in the future.) + it would be easy to add CFF support in the future with a PostScript + renderer.) This is not a complete font driver. Hooks must be supplied by the platform implementer to draw glyphs. */ @@ -45,11 +58,25 @@ struct sfnt_font_desc /* Style name of the font. */ Lisp_Object style; + /* Designer (foundry) of the font. */ + Lisp_Object designer; + /* Numeric width, weight, slant and spacing. */ int width, weight, slant, spacing; /* Path to the font file. */ char *path; + + /* char table consisting of characters already known to be + present in the font. */ + Lisp_Object char_cache; + + /* Whether or not the character map can't be used by Emacs. */ + bool cmap_invalid; + + /* The header of the cmap being used. May be invalid, in which case + platform_id will be 500. */ + struct sfnt_cmap_encoding_subtable subtable; }; /* List of fonts. */ @@ -94,19 +121,21 @@ sfnt_setup_coding_system (enum sfnt_platform_id id, int platform_specific_id, case SFNT_PLATFORM_MICROSOFT: system = Qutf_16be; - /* FIXME will someone look at the MS spec and see if this - right. */ - if (platform_specific_id - == SFNT_MICROSOFT_BIG_FIVE) + /* Not sure if this is right. */ + if (platform_specific_id == SFNT_MICROSOFT_BIG_FIVE) system = Qchinese_big5; break; + + default: + system = Qnil; } if (NILP (system)) return 1; setup_coding_system (system, coding); + return 0; } /* Globals used to communicate inside the condition-case wrapper. */ @@ -130,9 +159,9 @@ static Lisp_Object sfnt_font_dst_object; static bool sfnt_font_signal; static Lisp_Object -sfnt_safe_encode_coding_object_1 (void) +sfnt_safe_decode_coding_object_1 (void) { - encode_coding_object (sfnt_font_coding, + decode_coding_object (sfnt_font_coding, sfnt_font_src_object, sfnt_font_from, sfnt_font_from_byte, @@ -143,18 +172,18 @@ sfnt_safe_encode_coding_object_1 (void) } static Lisp_Object -sfnt_safe_encode_coding_object_2 (void) +sfnt_safe_decode_coding_object_2 (Lisp_Object error) { sfnt_font_signal = true; return Qnil; } -/* Like encode_coding_object, but return 1 if a signal happens. Value +/* Like decode_coding_object, but return 1 if a signal happens. Value is otherwise 0. */ static int -sfnt_safe_encode_coding_object (struct coding_system *coding, +sfnt_safe_decode_coding_object (struct coding_system *coding, Lisp_Object src_object, ptrdiff_t from, ptrdiff_t from_byte, ptrdiff_t to, ptrdiff_t to_byte, @@ -169,9 +198,9 @@ sfnt_safe_encode_coding_object (struct coding_system *coding, sfnt_font_dst_object = dst_object; sfnt_font_signal = false; - internal_condition_case (sfnt_safe_encode_coding_object_1, + internal_condition_case (sfnt_safe_decode_coding_object_1, Qt, - sfnt_safe_encode_coding_object_2); + sfnt_safe_decode_coding_object_2); return (int) sfnt_font_signal; } @@ -198,9 +227,8 @@ sfnt_decode_font_string (unsigned char *data, enum sfnt_platform_id id, coding.common_flags &= ~CODING_ANNOTATION_MASK; coding.source = data; - if (sfnt_safe_encode_coding_object (&coding, Qnil, 0, 0, - 0, 0, length, length, - Qnil)) + if (sfnt_safe_decode_coding_object (&coding, Qnil, 0, 0, + length, length, Qt)) return Qnil; return coding.dst_object; @@ -240,6 +268,28 @@ sfnt_decode_family_style (struct sfnt_name_table *name, return (!NILP (*family) && !NILP (*style)) ? 0 : 1; } +/* Decode the foundry names from the name table NAME. Return the + foundry name, or nil upon failure. */ + +static Lisp_Object +sfnt_decode_foundry_name (struct sfnt_name_table *name) +{ + struct sfnt_name_record designer_rec; + unsigned char *designer_data; + + designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER, + &designer_rec); + + if (!designer_data) + return Qnil; + + return sfnt_decode_font_string (designer_data, + designer_rec.platform_id, + designer_rec.platform_specific_id, + designer_rec.language_id, + designer_rec.length); +} + struct sfnt_style_desc { /* The C string to match against. */ @@ -250,7 +300,7 @@ struct sfnt_style_desc }; /* Array of style descriptions describing weight. */ -static struct sfnt_style_desc sfnt_weight_descriptions = +static struct sfnt_style_desc sfnt_weight_descriptions[] = { { "thin", 0, }, { "extralight", 40, }, @@ -271,14 +321,14 @@ static struct sfnt_style_desc sfnt_weight_descriptions = }; /* Array of style descriptions describing slant. */ -static struct sfnt_style_desc sfnt_slant_descriptions = +static struct sfnt_style_desc sfnt_slant_descriptions[] = { - { "italic", 100, }, - { "oblique", 110, }, + { "italic", 200, }, + { "oblique", 210, }, }; /* Array of style descriptions describing width. */ -static struct sfnt_width_desc sfnt_width_descriptions = +static struct sfnt_style_desc sfnt_width_descriptions[] = { { "ultracondensed", 50, }, { "extracondensed", 63, }, @@ -291,23 +341,24 @@ static struct sfnt_width_desc sfnt_width_descriptions = }; /* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing - based on the style name passed as STYLE. */ + based on the style name passed as STYLE_NAME. */ static void -sfnt_parse_style (Lisp_Object style, struct sfnt_font_desc *desc) +sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) { - char *style, single, *saveptr; + char *style, *single, *saveptr; int i; - /* Fill in default values. */ + /* Fill in default values. slant seems to not be consistent with + Fontconfig. */ desc->weight = 80; - desc->slant = 0; + desc->slant = 100; desc->width = 100; /* Split the style into spaces. As long as no weight, slant, or width is encountered, look in the corresponding descriptions array. GC must not happen inside this block. */ - style = SSDATA (Fdowncase (style)); + style = SSDATA (Fdowncase (style_name)); saveptr = NULL; while ((single = strtok_r (style, " ", &saveptr))) @@ -331,18 +382,18 @@ sfnt_parse_style (Lisp_Object style, struct sfnt_font_desc *desc) } } - if (!desc->slant) + if (desc->slant == 100) { /* Slant hasn't been found yet. Scan through the slant table. */ for (i = 0; i < ARRAYELTS (sfnt_slant_descriptions); ++i) { - if (!strcmp (sfnt_weight_descriptions[i].c_string, + if (!strcmp (sfnt_slant_descriptions[i].c_string, single)) { /* Slant found. Continue on reading the weight and width. */ - desc->slant = sfnt_weight_descriptions[i].value; + desc->slant = sfnt_slant_descriptions[i].value; goto next; } } @@ -359,7 +410,7 @@ sfnt_parse_style (Lisp_Object style, struct sfnt_font_desc *desc) { /* Width found. Continue on reading the slant and weight. */ - desc->slant = sfnt_width_descriptions[i].value; + desc->width = sfnt_width_descriptions[i].value; goto next; } } @@ -368,7 +419,7 @@ sfnt_parse_style (Lisp_Object style, struct sfnt_font_desc *desc) next: /* Break early if everything has been found. */ - if (desc->slant && desc->width != 100 && desc->weight != 80) + if (desc->slant != 100 && desc->width != 100 && desc->weight != 80) break; continue; @@ -395,7 +446,10 @@ sfnt_enum_font (const char *file) memcpy (desc->path, file, strlen (file) + 1); /* Now open the font for reading. */ - fd = emacs_open (file, O_RDWR); + fd = emacs_open (file, O_RDONLY, 0); + + if (fd == -1) + goto bail; /* Read the table directory. */ subtables = sfnt_read_table_directory (fd); @@ -426,6 +480,9 @@ sfnt_enum_font (const char *file) /* Set the family. */ desc->family = family; + desc->designer = sfnt_decode_foundry_name (name); + desc->char_cache = Qnil; + desc->subtable.platform_id = 500; /* Parse the style. */ sfnt_parse_style (style, desc); @@ -467,27 +524,1704 @@ sfnt_enum_font (const char *file) xfree (subtables); bail0: emacs_close (fd); + bail: + xfree (desc); return 1; } -void -syms_of_sfntfont (void) +/* Font discovery and matching. */ + +static struct charset * +sfntfont_charset_for_name (Lisp_Object symbol) { - DEFSYM (Qutf_16be, "utf-16be"); - DEFSYM (Qmac_roman, "mac-roman"); + ptrdiff_t idx; + int id; + + idx = CHARSET_SYMBOL_HASH_INDEX (symbol); + + if (idx == -1) + return NULL; + + /* Vcharset_hash_table is not a real variable, so Lisp programs + can't clobber it. */ + id = XFIXNUM (AREF (HASH_VALUE (XHASH_TABLE (Vcharset_hash_table), + idx), + charset_id)); + + return CHARSET_FROM_ID (id); } -void -mark_sfntfont (void) +/* Return the character set corresponding to a cmap subtable SUBTABLE. + Value is NULL if the subtable is not supported. */ + +static struct charset * +sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable) { - struct sfnt_font_desc *desc; + switch (subtable.platform_id) + { + case SFNT_PLATFORM_UNICODE: + /* Reject variation selector and last resort tables. */ + if ((subtable.platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (subtable.platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return NULL; - /* Mark each font desc. */ - for (desc = system_fonts; ++desc; desc = desc->next) + /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within + the BMP. */ + if (subtable.platform_specific_id < SFNT_UNICODE_2_0) + return sfntfont_charset_for_name (Qunicode_bmp); + + return sfntfont_charset_for_name (Qunicode); + + case SFNT_PLATFORM_MACINTOSH: + + switch (subtable.platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + return sfntfont_charset_for_name (Qmac_roman); + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return NULL; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (subtable.platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + /* Symbols in the Unicode PUA are still Unicode. */ + return sfntfont_charset_for_name (Qunicode); + + case SFNT_MICROSOFT_UNICODE_BMP: + return sfntfont_charset_for_name (Qunicode_bmp); + + case SFNT_MICROSOFT_SHIFT_JIS: + /* PCK aka japanese-jisx0208. */ + return sfntfont_charset_for_name (Qjapanese_jisx0208); + + case SFNT_MICROSOFT_PRC: + /* GBK, GB2312 or GB18030. */ + return sfntfont_charset_for_name (Qgbk); + + case SFNT_MICROSOFT_JOHAB: + /* KS C 5601-1992, aka korean-ksc5601. */ + return sfntfont_charset_for_name (Qkorean_ksc5601); + + case SFNT_MICROSOFT_UNICODE_UCS_4: + /* Unicode past the BMP. */ + return sfntfont_charset_for_name (Qucs); + } + + default: + return NULL; + } +} + +/* Return the type of characters that the cmap subtable SUBTABLE maps + from. Value is: + + 2 if SUBTABLE maps from Unicode characters, including those outside + the Unicode Basic Multilingual Plane (BMP). + + 1 if SUBTABLE maps from Unicode characters within the BMP. + + 0 if SUBTABLE maps from some other character set that Emacs knows + about. + + 3 if SUBTABLE cannot be used by Emacs. */ + +static int +sfntfont_identify_cmap (struct sfnt_cmap_encoding_subtable subtable) +{ + switch (subtable.platform_id) { - mark_object (desc->family); - mark_object (desc->style); + case SFNT_PLATFORM_UNICODE: + + /* Reject variation selector and last resort tables. */ + if ((subtable.platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (subtable.platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return 3; + + /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within + the BMP. */ + if (subtable.platform_specific_id < SFNT_UNICODE_2_0) + return 1; + + return 2; + + case SFNT_PLATFORM_MACINTOSH: + + switch (subtable.platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + /* mac-roman */ + return 0; + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return 3; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (subtable.platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + /* Symbols in the Unicode PUA are still Unicode. */ + return 1; + + case SFNT_MICROSOFT_UNICODE_BMP: + return 1; + + case SFNT_MICROSOFT_SHIFT_JIS: + /* PCK aka japanese-jisx0208. */ + return 0; + + case SFNT_MICROSOFT_PRC: + /* GBK, GB2312 or GB18030. */ + return 0; + + case SFNT_MICROSOFT_JOHAB: + /* KS C 5601-1992, aka korean-ksc5601. */ + return 0; + + case SFNT_MICROSOFT_UNICODE_UCS_4: + /* Unicode past the BMP. */ + return 2; + } + + default: + return 3; + } +} + +/* Pick the best character map in the cmap table CMAP. Use the + subtables in SUBTABLES and DATA. Return the subtable data and the + subtable in *SUBTABLE upon success, NULL otherwise. */ + +static struct sfnt_cmap_encoding_subtable_data * +sfntfont_select_cmap (struct sfnt_cmap_table *cmap, + struct sfnt_cmap_encoding_subtable *subtables, + struct sfnt_cmap_encoding_subtable_data **data, + struct sfnt_cmap_encoding_subtable *subtable) +{ + int i; + + /* First look for a non-BMP Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2) + { + *subtable = subtables[i]; + return data[i]; + } + } + + /* Next, look for a BMP only Unicode cmap. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1) + { + *subtable = subtables[i]; + return data[i]; + } + } + + /* Finally, use the first cmap that appears and can be + identified. */ + + for (i = 0; i < cmap->num_subtables; ++i) + { + if (data[i] && sfntfont_identify_cmap (subtables[i]) == 0) + { + *subtable = subtables[i]; + return data[i]; + } + } + + /* There are no cmaps available to Emacs. */ + return NULL; +} + +/* Read the cmap from the font descriptor DESC, and place it in CMAP. + Keep *CMAP untouched if opening the cmap fails. Set SUBTABLE to + the cmap's header upon success. */ + +static void +sfntfont_read_cmap (struct sfnt_font_desc *desc, + struct sfnt_cmap_encoding_subtable_data **cmap, + struct sfnt_cmap_encoding_subtable *subtable) +{ + struct sfnt_offset_subtable *font; + struct sfnt_cmap_encoding_subtable *subtables; + struct sfnt_cmap_encoding_subtable_data **data; + struct sfnt_cmap_table *table; + int fd, i; + + /* Pick a character map and place it in *CMAP. */ + fd = emacs_open (desc->path, O_RDONLY, 0); + + if (fd < 1) + return; + + font = sfnt_read_table_directory (fd); + + if (!font) + { + close (fd); + return; + } + + table = sfnt_read_cmap_table (fd, font, &subtables, + &data); + xfree (font); + + if (!table) + { + close (fd); + return; + } + + /* Now pick the best character map. */ + + *cmap = sfntfont_select_cmap (table, subtables, data, + subtable); + + /* Free the cmap data. */ + + for (i = 0; i < table->num_subtables; ++i) + { + if (data[i] != *cmap) + xfree (data[i]); + } + + xfree (data); + xfree (subtables); + xfree (table); +} + +/* Look up a character CHARACTER in the font description DESC. Cache + the results. Return true if the character exists, false otherwise. + + If *CMAP is NULL, select a character map for the font and save it + there. Otherwise, use the character map in *CMAP. Save data + associated with the character map in *SUBTABLE. */ + +static bool +sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character, + struct sfnt_cmap_encoding_subtable_data **cmap, + struct sfnt_cmap_encoding_subtable *subtable) +{ + Lisp_Object cached; + sfnt_char font_character; + struct charset *charset; + bool present; + + /* Return false for characters that don't fit in a char table. */ + if (XFIXNUM (character) > INT_MAX || XFIXNUM (character) < 0) + return false; + + if (!NILP (desc->char_cache)) + { + cached = char_table_ref (desc->char_cache, + XFIXNUM (character)); + if (!NILP (cached)) + return (EQ (cached, Qlambda) ? false : true); + } + + if (!*cmap && !desc->cmap_invalid) + sfntfont_read_cmap (desc, cmap, subtable); + + /* Check that a cmap is now present. */ + if (!*cmap) + { + /* Opening the cmap failed. Set desc->cmap_invalid to avoid + opening it again. */ + desc->cmap_invalid = true; + return false; + } + + /* Otherwise, encode the character. */ + + charset = sfntfont_charset_for_cmap (*subtable); + if (!charset) + /* Emacs missing charsets? */ + return false; + + font_character = ENCODE_CHAR (charset, XFIXNUM (character)); + + if (font_character == CHARSET_INVALID_CODE (charset)) + return false; + + /* Now return whether or not the glyph is present. */ + present = sfnt_lookup_glyph (font_character, *cmap) != 0; + + /* Cache the result. Store Qlambda when not present, Qt + otherwise. */ + + if (NILP (desc->char_cache)) + desc->char_cache = Fmake_char_table (Qfont_lookup_cache, + Qnil); + + Fset_char_table_range (desc->char_cache, character, + present ? Qt : Qlambda); + return present; +} + +/* Return whether or not the font description DESC satisfactorily + matches the font specification FONT_SPEC. */ + +static bool +sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) +{ + Lisp_Object tem, extra, tail; + struct sfnt_cmap_encoding_subtable_data *cmap; + size_t i; + struct sfnt_cmap_encoding_subtable subtable; + + /* cmap and subtable are caches for sfntfont_lookup_char. */ + + /* Check that the family name in SPEC matches DESC->family if it is + specified. */ + + tem = AREF (spec, FONT_FAMILY_INDEX); + + /* If TEM is a family listed in Vsfnt_default_family_alist, + then use that instead. */ + + if (SYMBOLP (tem) && CONSP (Vsfnt_default_family_alist)) + { + tail = Vsfnt_default_family_alist; + FOR_EACH_TAIL_SAFE (tail) + { + if (!CONSP (XCAR (tail))) + continue; + + if (STRINGP (XCAR (XCAR (tail))) + && STRINGP (XCDR (XCAR (tail))) + && Fstring_equal (SYMBOL_NAME (tem), + XCAR (XCAR (tail)))) + { + /* Special family found. */ + tem = Fintern (XCDR (XCAR (tail)), Qnil); + break; + } + } + } + + if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem), + desc->family))) + return false; + + /* Check that no adstyle has been specified. That's a relic from + the Postscript era. */ + + tem = AREF (spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem)) + return false; + + /* Check the style. */ + + if (FONT_WIDTH_NUMERIC (spec) != -1 + && FONT_WIDTH_NUMERIC (spec) != desc->width) + return false; + + if (FONT_WEIGHT_NUMERIC (spec) != -1 + && FONT_WEIGHT_NUMERIC (spec) != desc->weight) + return false; + + if (FONT_SLANT_NUMERIC (spec) != -1 + && FONT_SLANT_NUMERIC (spec) != desc->slant) + return false; + + /* Handle extras. */ + extra = AREF (spec, FONT_EXTRA_INDEX); + + if (NILP (extra)) + return true; + + tem = assq_no_quit (QCscript, extra); + cmap = NULL; + + if (!NILP (tem)) + { + /* If a script has been specified, look up its representative + characters and see if they are present in the font. This + requires reading the cmap. */ + tem = assq_no_quit (XCDR (tem), Vscript_representative_chars); + + if (CONSP (tem) && VECTORP (XCDR (tem))) + { + tem = XCDR (tem); + + /* The vector contains characters, of which one must be + present in the font. */ + for (i = 0; i < ASIZE (tem); ++i) + { + if (FIXNUMP (AREF (tem, i))) + { + if (!sfntfont_lookup_char (desc, AREF (tem, i), + &cmap, &subtable)) + goto fail; + + /* One character is enough to pass a font. Don't + look at too many. */ + break; + } + } + } + else if (CONSP (tem) && CONSP (XCDR (tem))) + { + tem = XCDR (tem); + + /* tem is a list of each characters, one of which must be + present in the font. */ + FOR_EACH_TAIL_SAFE (tem) + { + if (FIXNUMP (XCAR (tem))) + { + if (!sfntfont_lookup_char (desc, XCAR (tem), &cmap, + &subtable)) + goto fail; + + /* One character is enough to pass a font. Don't + look at too many. */ + break; + } + } + } + } + + /* Set desc->subtable if cmap was specified. */ + if (cmap) + desc->subtable = subtable; + + xfree (cmap); + return true; + + fail: + /* The cmap might've been read in and require deallocation. */ + xfree (cmap); + return false; +} + +/* Type of font entities and font objects created. */ +static Lisp_Object sfnt_vendor_name; + +/* Font driver used in font objects created. */ +static const struct font_driver *sfnt_font_driver; + +/* Return the font registry corresponding to the font descriptor DESC. + Under X, the font registry is an atom registered with the Open + Group uniquely identifying the organization which defines the + font's character set. + + In practice, the registry overlaps with the character set itself. + So Emacs just uses the ``registry'' field to represent both + instead. */ + +static Lisp_Object +sfntfont_registry_for_desc (struct sfnt_font_desc *desc) +{ + struct sfnt_cmap_encoding_subtable_data *cmap; + + cmap = NULL; + + if (desc->cmap_invalid) + return Qnil; + + if (desc->subtable.platform_id == 500) + { + /* Read in the cmap to determine the registry. */ + sfntfont_read_cmap (desc, &cmap, &desc->subtable); + + if (!cmap) + { + desc->cmap_invalid = true; + return Qnil; + } + } + + xfree (cmap); + + if (desc->subtable.platform_id != 500) + { + /* desc->subtable.platform_id is now set. CMAP is already free, + because it is not actually used. */ + + switch (desc->subtable.platform_id) + { + case SFNT_PLATFORM_UNICODE: + /* Reject variation selector and last resort tables. */ + if ((desc->subtable.platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + || (desc->subtable.platform_specific_id + == SFNT_UNICODE_LAST_RESORT)) + return Qnil; + + return Qiso10646_1; + + case SFNT_PLATFORM_MACINTOSH: + + switch (desc->subtable.platform_specific_id) + { + case SFNT_MACINTOSH_ROMAN: + /* X calls mac-roman ``apple-roman''. */ + return Qapple_roman; + + default: + /* Some other Macintosh charset not supported by Emacs. */ + return Qnil; + } + + case SFNT_PLATFORM_MICROSOFT: + + /* Microsoft specific encodings. */ + + switch (desc->subtable.platform_specific_id) + { + case SFNT_MICROSOFT_SYMBOL: + case SFNT_MICROSOFT_UNICODE_BMP: + /* Symbols in the Unicode PUA are still Unicode. */ + return Qiso10646_1; + + case SFNT_MICROSOFT_SHIFT_JIS: + return Qjisx0208_1983_0; + + case SFNT_MICROSOFT_PRC: + return Qgbk; + + case SFNT_MICROSOFT_JOHAB: + return Qksc5601_1987_0; + + case SFNT_MICROSOFT_UNICODE_UCS_4: + return Qiso10646_1; + } + + default: + return Qnil; + } } + + return Qnil; +} + +/* Return a font-entity that represents the font descriptor (unopened + font) DESC. */ + +static Lisp_Object +sfntfont_desc_to_entity (struct sfnt_font_desc *desc) +{ + Lisp_Object entity; + + entity = font_make_entity (); + + ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name); + + if (!NILP (desc->designer)) + ASET (entity, FONT_FOUNDRY_INDEX, + Fintern (desc->designer, Qnil)); + + ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); + ASET (entity, FONT_ADSTYLE_INDEX, Qnil); + ASET (entity, FONT_REGISTRY_INDEX, + sfntfont_registry_for_desc (desc)); + + /* Size of 0 means the font is scalable. */ + ASET (entity, FONT_SIZE_INDEX, make_fixnum (0)); + ASET (entity, FONT_AVGWIDTH_INDEX, make_fixnum (0)); + ASET (entity, FONT_SPACING_INDEX, + make_fixnum (desc->spacing)); + + FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, + make_fixnum (desc->width)); + FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, + make_fixnum (desc->weight)); + FONT_SET_STYLE (entity, FONT_SLANT_INDEX, + make_fixnum (desc->slant)); + + ASET (entity, FONT_ADSTYLE_INDEX, Qnil); + + /* Set FONT_EXTRA_INDEX to a pointer to the font description. Font + descriptions are never supposed to be freed. */ + ASET (entity, FONT_EXTRA_INDEX, + list1 (Fcons (Qfont_entity, make_mint_ptr (desc)))); + + return entity; +} + +/* Return a list of font-entities matching the specified + FONT_SPEC. */ + +Lisp_Object +sfntfont_list (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object matching, tem; + struct sfnt_font_desc *desc; + + matching = Qnil; + + block_input (); + /* Returning irrelevant results on receiving an OTF form will cause + fontset.c to loop over and over, making displaying some + characters very slow. */ + tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX)); + if (CONSP (tem) && !NILP (XCDR (tem))) + { + unblock_input (); + return Qnil; + } + + /* Loop through known system fonts and add them one-by-one. */ + for (desc = system_fonts; desc; desc = desc->next) + { + if (sfntfont_list_1 (desc, font_spec)) + matching = Fcons (sfntfont_desc_to_entity (desc), matching); + } + + unblock_input (); + + return matching; +} + +/* Return the first font-entity matching the specified FONT_SPEC. */ + +Lisp_Object +sfntfont_match (struct frame *f, Lisp_Object font_spec) +{ + Lisp_Object matches; + + matches = sfntfont_list (f, font_spec); + + if (!NILP (matches)) + return XCAR (matches); + + return Qnil; +} + + + +enum + { + SFNT_OUTLINE_CACHE_SIZE = 128, + SFNT_RASTER_CACHE_SIZE = 100, + }; + +/* Caching subsystem. Generating outlines from glyphs is expensive, + and so is rasterizing them, so two caches are maintained for both + glyph outlines and rasters. */ + +struct sfnt_outline_cache +{ + /* Next and last cache buckets. */ + struct sfnt_outline_cache *next, *last; + + /* Pointer to outline. */ + struct sfnt_glyph_outline *outline; + + /* What glyph this caches. */ + sfnt_glyph glyph; +}; + +struct sfnt_raster_cache +{ + /* Next and last cache buckets. */ + struct sfnt_raster_cache *next, *last; + + /* Pointer to raster. */ + struct sfnt_raster *raster; + + /* What glyph this caches. */ + sfnt_glyph glyph; +}; + +struct sfntfont_get_glyph_outline_dcontext +{ + /* Long and short loca tables. */ + struct sfnt_loca_table_long *loca_long; + struct sfnt_loca_table_short *loca_short; + + /* glyf table. */ + struct sfnt_glyf_table *glyf; +}; + +/* Return the glyph identified by GLYPH from the glyf and loca table + specified in DCONTEXT. Set *NEED_FREE to true. */ + +static struct sfnt_glyph * +sfntfont_get_glyph (sfnt_glyph glyph, void *dcontext, + bool *need_free) +{ + struct sfntfont_get_glyph_outline_dcontext *tables; + + tables = dcontext; + *need_free = true; + + return sfnt_read_glyph (glyph, tables->glyf, + tables->loca_short, + tables->loca_long); +} + +/* Free the glyph identified by GLYPH. */ + +static void +sfntfont_free_glyph (struct sfnt_glyph *glyph, void *dcontext) +{ + sfnt_free_glyph (glyph); +} + +/* Dereference the outline OUTLINE. Free it once refcount reaches + 0. */ + +static void +sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) +{ + eassert (outline->refcount > 0); + + if (--outline->refcount) + return; + + xfree (outline); +} + +/* Get the outline corresponding to the specified GLYPH_CODE in CACHE. + Use the pixel size PIXEL_SIZE, the glyf table GLYF, and the head + table HEAD. Keep *CACHE_SIZE updated with the number of elements + in the cache. + + Use the offset information in the long or short loca tables + LOCA_LONG and LOCA_SHORT, whichever is set. + + Return the outline with an incremented reference count and enter + the generated outline into CACHE upon success, possibly discarding + any older outlines, or NULL on failure. */ + +static struct sfnt_glyph_outline * +sfntfont_get_glyph_outline (sfnt_glyph glyph_code, + struct sfnt_outline_cache *cache, + int pixel_size, int *cache_size, + struct sfnt_glyf_table *glyf, + struct sfnt_head_table *head, + struct sfnt_loca_table_short *loca_short, + struct sfnt_loca_table_long *loca_long) +{ + struct sfnt_outline_cache *start; + struct sfnt_glyph_outline *outline; + struct sfnt_glyph *glyph; + struct sfntfont_get_glyph_outline_dcontext dcontext; + + start = cache->next; + + /* See if the outline is already cached. */ + for (; start != cache; start = start->next) + { + if (start->glyph == glyph_code) + { + /* Move start to the start of the ring. Then increase + start->outline->refcount and return it. */ + + start->last->next = start->next; + start->next->last = start->last; + + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + start->outline->refcount++; + + return start->outline; + } + } + + /* Not already cached. Get the glyph. */ + glyph = sfnt_read_glyph (glyph_code, glyf, + loca_short, loca_long); + + if (!glyph) + return NULL; + + dcontext.loca_long = loca_long; + dcontext.loca_short = loca_short; + dcontext.glyf = glyf; + + outline = sfnt_build_glyph_outline (glyph, head, pixel_size, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); + xfree (glyph); + + if (!outline) + return NULL; + + start = xmalloc (sizeof *start); + start->glyph = glyph_code; + start->outline = outline; + + /* One reference goes to the cache. The second reference goes to + the caller. */ + outline->refcount = 2; + + /* Link start onto the cache. */ + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + + /* Update the cache size. */ + (*cache_size)++; + + /* Figure out if the least recently used element has to be + evicted. */ + if (*cache_size > SFNT_OUTLINE_CACHE_SIZE) + { + start = cache->last; + eassert (start != cache); + + /* Free the least recently used entry in the cache. */ + start->last->next = start->next; + start->next->last = start->last; + sfntfont_dereference_outline (start->outline); + xfree (start); + + (*cache_size)--; + } + + /* Return the cached outline. */ + return outline; +} + +/* Free the outline cache referred to by CACHE. Dereference each + outline contained therein. */ + +static void +sfntfont_free_outline_cache (struct sfnt_outline_cache *cache) +{ + struct sfnt_outline_cache *next, *last; + + /* Handle partly initialized fonts. */ + if (!cache->next) + return; + + for (next = cache->next; next != cache;) + { + last = next; + next = next->next; + + sfntfont_dereference_outline (last->outline); + xfree (last); + } +} + +/* Dereference the raster RASTER. Free it once refcount reaches + 0. */ + +static void +sfntfont_dereference_raster (struct sfnt_raster *raster) +{ + eassert (raster->refcount > 0); + + if (--raster->refcount) + return; + + xfree (raster); +} + +/* Get the raster corresponding to the specified GLYPH_CODE in CACHE. + Use the outline named OUTLINE. Keep *CACHE_SIZE updated with the + number of elements in the cache. */ + +static struct sfnt_raster * +sfntfont_get_glyph_raster (sfnt_glyph glyph_code, + struct sfnt_raster_cache *cache, + struct sfnt_glyph_outline *outline, + int *cache_size) +{ + struct sfnt_raster_cache *start; + struct sfnt_raster *raster; + + /* See if the raster is already cached. */ + start = cache->next; + + for (; start != cache; start = start->next) + { + if (start->glyph == glyph_code) + { + /* Move start to the start of the ring. Them, increase + start->raster->refcount and return it. */ + + start->last->next = start->next; + start->next->last = start->last; + + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + start->raster->refcount++; + + return start->raster; + } + } + + /* Not already cached. Raster the outline. */ + raster = sfnt_raster_glyph_outline (outline); + + if (!raster) + return NULL; + + start = xmalloc (sizeof *start); + start->glyph = glyph_code; + start->raster = raster; + + /* One reference goes to the cache. The second reference goes to + the caller. */ + raster->refcount = 2; + + /* Link start onto the cache. */ + start->next = cache->next; + start->last = cache; + start->next->last = start; + start->last->next = start; + + /* Update the cache size. */ + (*cache_size)++; + + /* Figure out if the least recently used element has to be + evicted. */ + if (*cache_size > SFNT_OUTLINE_CACHE_SIZE) + { + start = cache->last; + eassert (start != cache); + + /* Free the least recently used entry in the cache. */ + start->last->next = start->next; + start->next->last = start->last; + sfntfont_dereference_raster (start->raster); + xfree (start); + + (*cache_size)--; + } + + /* Return the cached raster. */ + return raster; +} + +/* Free the raster cache referred to by CACHE. Dereference each + raster contained therein. */ + +static void +sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) +{ + struct sfnt_raster_cache *next, *last; + + /* Handle partly initialized fonts. */ + if (!cache->next) + return; + + for (next = cache->next; next != cache;) + { + last = next; + next = next->next; + + sfntfont_dereference_raster (last->raster); + xfree (last); + } +} + + + +/* Opening fonts. */ + +struct sfnt_font_info +{ + /* Parent font structure. */ + struct font font; + + /* Various tables required to use the font. */ + struct sfnt_cmap_table *cmap; + struct sfnt_hhea_table *hhea; + struct sfnt_maxp_table *maxp; + struct sfnt_head_table *head; + struct sfnt_hmtx_table *hmtx; + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; + + /* The selected character map. */ + struct sfnt_cmap_encoding_subtable_data *cmap_data; + + /* Data identifying that character map. */ + struct sfnt_cmap_encoding_subtable cmap_subtable; + + /* Outline cache. */ + struct sfnt_outline_cache outline_cache; + + /* Number of elements in the outline cache. */ + int outline_cache_size; + + /* Raster cache. */ + struct sfnt_raster_cache raster_cache; + + /* Number of elements in the raster cache. */ + int raster_cache_size; +}; + +/* Look up the glyph corresponding to the character C in FONT. Return + 0 upon failure, and the glyph otherwise. */ + +static sfnt_glyph +sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c) +{ + struct charset *charset; + sfnt_char character; + sfnt_glyph glyph; + + charset = CHARSET_FROM_ID (font_info->font.encoding_charset); + + if (!charset) + return 0; + + character = ENCODE_CHAR (charset, c); + + if (character == CHARSET_INVALID_CODE (charset)) + return 0; + + /* Do the actual lookup with the encoded character. */ + glyph = sfnt_lookup_glyph (character, font_info->cmap_data); + + return glyph; +} + +/* Probe and set FONT_INFO->font.average_width, + FONT_INFO->font.space_width, and FONT_INFO->font.min_width + according to the tables contained therein. */ + +static void +sfntfont_probe_widths (struct sfnt_font_info *font_info) +{ + int i, num_characters, total_width; + sfnt_glyph glyph; + struct sfnt_glyph_metrics metrics; + + num_characters = 0; + total_width = 0; + + /* First set some reasonable default values. */ + font_info->font.average_width = font_info->font.pixel_size; + font_info->font.space_width = font_info->font.pixel_size; + font_info->font.min_width = 1; + + /* Next, loop through the common ASCII characters. Tally up their + advance widths and set space_width if necessary. */ + for (i = 0; i < 127; ++i) + { + glyph = sfntfont_lookup_glyph (font_info, i); + + if (!glyph) + continue; + + /* Now look up the metrics of this glyph. */ + if (sfnt_lookup_glyph_metrics (glyph, font_info->font.pixel_size, + &metrics, font_info->hmtx, + font_info->hhea, font_info->head, + font_info->maxp)) + continue; + + /* Increase the number of characters. */ + num_characters++; + + /* Add the advance to total_width. */ + total_width += metrics.advance >> 16; + + /* Update min_width if it hasn't been set yet or is wider. */ + if (font_info->font.min_width == 1 + || font_info->font.min_width > metrics.advance >> 16) + font_info->font.min_width = metrics.advance >> 16; + + /* If i is the space character, set the space width. Make sure + to round this up. */ + if (i == 32) + font_info->font.space_width + = SFNT_CEIL_FIXED (metrics.advance) >> 16; + } + + /* Now, if characters were found, set average_width. */ + if (num_characters) + font_info->font.average_width = total_width / num_characters; +} + +/* Open the font corresponding to the font-entity FONT_ENTITY. Return + nil upon failure, else the opened font-object. */ + +Lisp_Object +sfntfont_open (struct frame *f, Lisp_Object font_entity, + int pixel_size) +{ + struct sfnt_font_info *font_info; + struct font *font; + struct sfnt_font_desc *desc; + Lisp_Object font_object; + int fd, i; + struct sfnt_offset_subtable *subtable; + struct sfnt_cmap_encoding_subtable *subtables; + struct sfnt_cmap_encoding_subtable_data **data; + struct charset *charset; + + 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; + } + + /* Now find the font description corresponding to FONT_ENTITY. */ + + if (NILP (AREF (font_entity, FONT_EXTRA_INDEX))) + return Qnil; + + desc = xmint_pointer (XCDR (XCAR (AREF (font_entity, FONT_EXTRA_INDEX)))); + + /* Build the font object. */ + font_object = font_make_object (VECSIZE (struct sfnt_font_info), + font_entity, pixel_size); + font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object); + + block_input (); + + /* Initialize all the font driver specific data. */ + + font_info->cmap = NULL; + font_info->hhea = NULL; + font_info->maxp = NULL; + font_info->head = NULL; + font_info->glyf = NULL; + font_info->hmtx = NULL; + font_info->loca_short = NULL; + font_info->loca_long = NULL; + font_info->cmap_data = NULL; + + font_info->outline_cache.next = &font_info->outline_cache; + font_info->outline_cache.last = &font_info->outline_cache; + font_info->outline_cache_size = 0; + font_info->raster_cache.next = &font_info->raster_cache; + font_info->raster_cache.last = &font_info->raster_cache; + font_info->raster_cache_size = 0; + + /* Open the font. */ + fd = emacs_open (desc->path, O_RDONLY, 0); + + if (fd == -1) + goto bail; + + /* Read the offset subtable. */ + subtable = sfnt_read_table_directory (fd); + + if (!subtable) + goto bail1; + + /* Read required tables. This font backend is supposed to be used + mostly on devices with flash memory, so the order in which they + are read is insignificant. */ + + /* Select a character map table. */ + font_info->cmap = sfnt_read_cmap_table (fd, subtable, &subtables, + &data); + if (!font_info->cmap) + goto bail2; + + font_info->cmap_data + = sfntfont_select_cmap (font_info->cmap, + subtables, data, + &font_info->cmap_subtable); + + for (i = 0; i < font_info->cmap->num_subtables; ++i) + { + if (data[i] != font_info->cmap_data) + xfree (data[i]); + } + + xfree (subtables); + xfree (data); + + if (!font_info->cmap_data) + goto bail3; + + /* Read the hhea, maxp, glyf, and head tables. */ + font_info->hhea = sfnt_read_hhea_table (fd, subtable); + font_info->maxp = sfnt_read_maxp_table (fd, subtable); + font_info->glyf = sfnt_read_glyf_table (fd, subtable); + font_info->head = sfnt_read_head_table (fd, subtable); + + /* If any of those tables couldn't be read, bail. */ + if (!font_info->hhea || !font_info->maxp || !font_info->glyf + || !font_info->head) + goto bail4; + + /* Now figure out which kind of loca table must be read based on + head->index_to_loc_format. */ + font_info->loca_short = NULL; + font_info->loca_long = NULL; + + if (font_info->head->index_to_loc_format) + { + font_info->loca_long + = sfnt_read_loca_table_long (fd, subtable); + + if (!font_info->loca_long) + goto bail4; + } + else + { + font_info->loca_short + = sfnt_read_loca_table_short (fd, subtable); + + if (!font_info->loca_short) + goto bail4; + } + + /* Read the horizontal metrics table. */ + font_info->hmtx = sfnt_read_hmtx_table (fd, subtable, + font_info->hhea, + font_info->maxp); + if (!font_info->hmtx) + goto bail5; + + /* Fill in font data. */ + font = &font_info->font; + font->pixel_size = pixel_size; + font->driver = sfnt_font_driver; + font->encoding_charset = font->repertory_charset = -1; + + /* Figure out which character set to use. */ + charset = sfntfont_charset_for_cmap (font_info->cmap_subtable); + + if (!charset) + goto bail6; + + /* Set the character set IDs. */ + font->encoding_charset = charset->id; + font->repertory_charset = charset->id; + + /* Figure out the font ascent and descent. */ + font->ascent + = ceil (font_info->hhea->ascent + * pixel_size * 1.0 / font_info->head->units_per_em); + font->descent + = -floor (font_info->hhea->descent + * pixel_size * 1.0 / font_info->head->units_per_em); + font->height = font->ascent + font->descent; + + /* Set generic attributes such as type and style. */ + ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name); + + if (!NILP (desc->designer)) + ASET (font_object, FONT_FOUNDRY_INDEX, + Fintern (desc->designer, Qnil)); + + ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); + ASET (font_object, FONT_ADSTYLE_INDEX, Qnil); + ASET (font_object, FONT_REGISTRY_INDEX, + sfntfont_registry_for_desc (desc)); + + /* Size of 0 means the font is scalable. */ + ASET (font_object, FONT_SIZE_INDEX, make_fixnum (0)); + ASET (font_object, FONT_AVGWIDTH_INDEX, make_fixnum (0)); + ASET (font_object, FONT_SPACING_INDEX, + make_fixnum (desc->spacing)); + + FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX, + make_fixnum (desc->width)); + FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX, + make_fixnum (desc->weight)); + FONT_SET_STYLE (font_object, FONT_SLANT_INDEX, + make_fixnum (desc->slant)); + + ASET (font_object, FONT_ADSTYLE_INDEX, Qnil); + + /* Find out the minimum, maximum and average widths. */ + sfntfont_probe_widths (font_info); + + /* Clear various offsets. */ + font_info->font.baseline_offset = 0; + font_info->font.relative_compose = 0; + font_info->font.default_ascent = 0; + font_info->font.vertical_centering = 0; + font_info->font.underline_position = -1; + font_info->font.underline_thickness = 0; + + /* Calculate the xfld name. */ + font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); + + /* All done. */ + unblock_input (); + return font_object; + + bail6: + xfree (font_info->hmtx); + font_info->hmtx = NULL; + bail5: + xfree (font_info->loca_long); + xfree (font_info->loca_short); + font_info->loca_long = NULL; + font_info->loca_short = NULL; + bail4: + xfree (font_info->hhea); + xfree (font_info->maxp); + xfree (font_info->glyf); + xfree (font_info->head); + font_info->hhea = NULL; + font_info->maxp = NULL; + font_info->glyf = NULL; + font_info->head = NULL; + + /* This comes in bail4 due to a peculiarity of how the four tables + above are validated. */ + xfree (font_info->cmap_data); + font_info->cmap_data = NULL; + bail3: + xfree (font_info->cmap); + font_info->cmap = NULL; + bail2: + xfree (subtable); + bail1: + close (fd); + bail: + unblock_input (); + return Qnil; +} + + + +/* Metrics computation and other similar font backend functions. */ + +/* Return the glyph code corresponding to C inside the font-object + FONT. Value is the glyph code upon success, else + FONT_INVALID_CODE. */ + +unsigned int +sfntfont_encode_char (struct font *font, int c) +{ + sfnt_glyph glyph; + + /* Now look up the glyph. */ + glyph = sfntfont_lookup_glyph ((struct sfnt_font_info *) font, c); + + if (!glyph) + return FONT_INVALID_CODE; + + return glyph; +} + +/* Measure the single glyph GLYPH in the font FONT and return its + metrics in *PCM. Value is 0 upon success, 1 otherwise. */ + +static int +sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, + struct font_metrics *pcm) +{ + struct sfnt_glyph_metrics metrics; + struct sfnt_glyph_outline *outline; + + /* Get the glyph metrics first. */ + if (sfnt_lookup_glyph_metrics (glyph, font->font.pixel_size, + &metrics, font->hmtx, font->hhea, + font->head, font->maxp)) + return 1; + + /* Now get the glyph outline, which is required to obtain the rsb, + ascent and descent. */ + outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache, + font->font.pixel_size, + &font->outline_cache_size, + font->glyf, font->head, + font->loca_short, + font->loca_long); + + if (!outline) + return 1; + + /* How to round lbearing and rbearing? */ + pcm->lbearing = metrics.lbearing >> 16; + pcm->rbearing = outline->xmax >> 16; + + /* Round the advance, ascent and descent upwards. */ + pcm->width = SFNT_CEIL_FIXED (metrics.advance) >> 16; + pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) >> 16; + pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) >> 16; + + sfntfont_dereference_outline (outline); + return 0; +} + +/* Return the total text extents of NGLYPHS glyphs given as CODE in + the single font metrics array METRICS. */ + +void +sfntfont_text_extents (struct font *font, const unsigned int *code, + int nglyphs, struct font_metrics *metrics) +{ + int i, total_width; + struct font_metrics pcm; + + total_width = 0; + + /* Get the metrcs one by one, then sum them up. */ + for (i = 0; i < nglyphs; ++i) + { + if (!sfntfont_measure_pcm ((struct sfnt_font_info *) font, + code[i], &pcm)) + { + /* Add the per-char metric (PCM) to the metrics in + METRICS. */ + + if (total_width + pcm.lbearing < metrics->lbearing) + metrics->lbearing = total_width + pcm.lbearing; + + if (total_width + pcm.rbearing > metrics->rbearing) + metrics->rbearing = total_width + pcm.rbearing; + + if (pcm.ascent > metrics->ascent) + metrics->ascent = pcm.ascent; + + if (pcm.descent > metrics->descent) + metrics->descent = pcm.descent; + } + + total_width += pcm.width; + } + + metrics->width = total_width; +} + +/* Close the font FONT, discarding all tables inside it and + dereferencing all cached outlines and rasters. */ + +void +sfntfont_close (struct font *font) +{ + struct sfnt_font_info *info; + + info = (struct sfnt_font_info *) font; + xfree (info->cmap); + xfree (info->hhea); + xfree (info->maxp); + xfree (info->head); + xfree (info->hmtx); + xfree (info->glyf); + xfree (info->loca_short); + xfree (info->loca_long); + xfree (info->cmap_data); + + sfntfont_free_outline_cache (&info->outline_cache); + sfntfont_free_raster_cache (&info->raster_cache); +} + + + +/* Glyph display. */ + +/* Function called to actually draw rasters to the glass. */ +static sfntfont_put_glyph_proc sfnt_put_glyphs; + +/* Draw glyphs in S->char2b starting from FROM to TO, with the origin + at X and baseline at Y. Fill the background from X, Y + + FONT_DESCENT to X + S->background_width, Y - FONT_ASCENT with the + background color if necessary. Use the foreground and background + colors in S->gc. */ + +int +sfntfont_draw (struct glyph_string *s, int from, int to, + int x, int y, bool with_background) +{ + int length; + struct sfnt_raster **rasters; + int *x_coords, current_x, i; + struct sfnt_glyph_outline *outline; + struct font *font; + struct sfnt_font_info *info; + struct sfnt_glyph_metrics metrics; + + length = to - from; + font = s->face->font; + info = (struct sfnt_font_info *) font; + + rasters = alloca (length * sizeof *rasters); + x_coords = alloca (length * sizeof *x_coords); + current_x = x; + + /* Get rasters and outlines for them. */ + for (i = from; i < to; ++i) + { + /* Look up the metrics for this glyph. */ + if (sfnt_lookup_glyph_metrics (s->char2b[i], font->pixel_size, + &metrics, info->hmtx, info->hhea, + info->head, info->maxp)) + { + rasters[i - from] = NULL; + x_coords[i - from] = 0; + continue; + } + + /* Look up the outline. */ + outline = sfntfont_get_glyph_outline (s->char2b[i], + &info->outline_cache, + font->pixel_size, + &info->outline_cache_size, + info->glyf, info->head, + info->loca_short, + info->loca_long); + x_coords[i - from] = 0; + + if (!outline) + { + rasters[i - from] = NULL; + continue; + } + + /* Rasterize the outline. */ + rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i], + &info->raster_cache, + outline, + &info->raster_cache_size); + sfntfont_dereference_outline (outline); + + if (!rasters[i - from]) + continue; + + /* Now work out where to put the outline. */ + x_coords[i - from] = current_x; + current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16; + } + + /* Call the window system function to put the glyphs to the + frame. */ + sfnt_put_glyphs (s, from, to, x, y, with_background, + rasters, x_coords); + + /* Dereference all the rasters. */ + for (i = 0; i < from - to; ++i) + { + if (rasters[i]) + sfntfont_dereference_raster (rasters[i]); + } + + return 1; +} + + + +/* Other callbacks. */ + +/* Return a list of each font family known to Emacs. F is supposed to + be a frame but is ignored. */ + +Lisp_Object +sfntfont_list_family (struct frame *f) +{ + Lisp_Object families; + struct sfnt_font_desc *desc; + + families = Qnil; + + for (desc = system_fonts; desc; desc = desc->next) + /* Add desc->family to the list. */ + families = Fcons (desc->family, families); + + /* Not sure if deleting duplicates is worth it. Is this ever + called? */ + return families; +} + + + +void +syms_of_sfntfont (void) +{ + DEFSYM (Qutf_16be, "utf-16be"); + DEFSYM (Qmac_roman, "mac-roman"); + DEFSYM (Qchinese_big5, "chinese-big5"); + DEFSYM (Qunicode_bmp, "unicode-bmp"); + DEFSYM (Qucs, "ucs"); + DEFSYM (Qjapanese_jisx0208, "japanese-jisx0208"); + DEFSYM (Qgbk, "gbk"); + DEFSYM (Qkorean_ksc5601, "korean-ksc5601"); + DEFSYM (Qapple_roman, "apple-roman"); + DEFSYM (Qjisx0208_1983_0, "jisx0208.1983-0"); + DEFSYM (Qksc5601_1987_0, "ksc5601.1987-0"); + + /* Char-table purpose. */ + DEFSYM (Qfont_lookup_cache, "font-lookup-cache"); + + /* Set up staticpros. */ + sfnt_vendor_name = Qnil; + staticpro (&sfnt_vendor_name); + + /* This variable is supposed to be set by the platform specific part + of the font backend. */ + DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist, + doc: /* Alist between "emulated" and actual font family names. + +Much Emacs code assumes that font families named "Monospace" and "Sans +Serif" exist, and map to the default monospace and Sans Serif fonts on +a system. When the `sfnt' font driver is asked to look for a font +with one of the families in this alist, it uses its value instead. */); + Vsfnt_default_family_alist = Qnil; +} + +void +mark_sfntfont (void) +{ + struct sfnt_font_desc *desc; + + /* Mark each font desc. */ + for (desc = system_fonts; desc; desc = desc->next) + { + mark_object (desc->family); + mark_object (desc->style); + mark_object (desc->char_cache); + mark_object (desc->designer); + } +} + +void +init_sfntfont (void) +{ + +} + + + +/* Initialize the sfntfont font driver. VENDOR_TYPE is the type of + all font entities created. DRIVER is the font driver that is saved + in font objects. PUT_GLYPHS is a function that is called with 8 + arguments, S, FROM, TO, X, Y, WITH_BACKGROUND, RASTERS, and + X_COORDS, and should draw all the rasters in RASTERS to S->f, + originating at X_COORDS[i], Y, along with filling the background if + WITH_BACKGROUND is specified. */ + +void +init_sfntfont_vendor (Lisp_Object vendor_name, + const struct font_driver *driver, + sfntfont_put_glyph_proc put_glyphs) +{ + sfnt_vendor_name = vendor_name; + sfnt_font_driver = driver; + sfnt_put_glyphs = put_glyphs; } diff --git a/src/sfntfont.h b/src/sfntfont.h index 3ce60f83984..f49121438a5 100644 --- a/src/sfntfont.h +++ b/src/sfntfont.h @@ -21,6 +21,39 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _SFNTFONT_H_ #define _SFNTFONT_H_ +#include "lisp.h" +#include "frame.h" +#include "font.h" +#include "sfnt.h" + extern int sfnt_enum_font (const char *); + +/* Font driver callbacks. */ + +extern Lisp_Object sfntfont_list (struct frame *, Lisp_Object); +extern Lisp_Object sfntfont_match (struct frame *, Lisp_Object); +extern Lisp_Object sfntfont_open (struct frame *, Lisp_Object, int); + +extern unsigned int sfntfont_encode_char (struct font *, int); +extern void sfntfont_text_extents (struct font *, const unsigned int *, + int, struct font_metrics *); +extern void sfntfont_close (struct font *); +extern int sfntfont_draw (struct glyph_string *, int, int, + int, int, bool); +extern Lisp_Object sfntfont_list_family (struct frame *); + + +/* Initialization functions. */ + +typedef void (*sfntfont_put_glyph_proc) (struct glyph_string *, int, int, + int, int, bool, struct sfnt_raster **, + int *); + +extern void syms_of_sfntfont (void); +extern void init_sfntfont (void); +extern void mark_sfntfont (void); +extern void init_sfntfont_vendor (Lisp_Object, const struct font_driver *, + sfntfont_put_glyph_proc); + #endif /* _SFNTFONT_H_ */ diff --git a/src/xdisp.c b/src/xdisp.c index 845bb4b2fb0..b9ee102af26 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -36227,6 +36227,55 @@ gui_intersect_rectangles (const Emacs_Rectangle *r1, const Emacs_Rectangle *r2, return intersection_p; } +/* EXPORT: + Determine the union of the rectangles A and B. Return the smallest + rectangle encompassing both the bounds of A and B in *RESULT. It + is safe for all three arguments to point to each other. */ + +void +gui_union_rectangles (const Emacs_Rectangle *a, const Emacs_Rectangle *b, + Emacs_Rectangle *result) +{ + struct gui_box a_box, b_box, result_box; + + /* Handle special cases where one of the rectangles is empty. */ + + if (!a->width || !a->height) + { + *result = *b; + return; + } + else if (!b->width || !b->height) + { + *result = *a; + return; + } + + /* Convert A and B to boxes. */ + a_box.x1 = a->x; + a_box.y1 = a->y; + a_box.x2 = a->x + a->width; + a_box.y2 = a->y + a->height; + + b_box.x1 = b->x; + b_box.y1 = b->y; + b_box.x2 = b->x + b->width; + b_box.y2 = b->y + b->height; + + /* Compute the union of the boxes. */ + result_box.x1 = min (a_box.x1, b_box.x1); + result_box.y1 = min (a_box.y1, b_box.y1); + result_box.x2 = max (a_box.x2, b_box.x2); + result_box.y2 = max (a_box.y2, b_box.y2); + + /* Convert result_box to an XRectangle and put the result in + RESULT. */ + result->x = result_box.x1; + result->y = result_box.y1; + result->width = result_box.x2 - result_box.x1; + result->height = result_box.y2 - result_box.y1; +} + #endif /* HAVE_WINDOW_SYSTEM */ -- 2.39.5