From 2d7622491884843ce15e2717675b128af1bdbefc Mon Sep 17 00:00:00 2001 From: Ali Alzyod Date: Thu, 12 Dec 2019 16:22:12 +0900 Subject: [PATCH] evas_object_textblock: add support for variation sequences Summary: update font processing to handle variation sequences unicodes to select proper glypg in respect to variation seqences Test Plan: ``` #define EFL_EO_API_SUPPORT 1 #define EFL_BETA_API_SUPPORT 1 #include #include #include EAPI_MAIN int elm_main(int argc, char **argv) { Evas_Object *win, *textblock; elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); win = elm_win_util_standard_add("Main", ""); elm_win_autodel_set(win, EINA_TRUE); textblock = evas_object_textblock_add(win); efl_canvas_text_style_set(textblock,NULL,"DEFAULT='font=DejaVuSans font_fallbacks=SamsungColorEmoji color=#000 font_size=20'"); evas_object_textblock_text_markup_set(textblock, "8️⃣☪️AAA☪︎1234567️⃣"); evas_object_size_hint_weight_set(textblock, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(textblock, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(textblock); evas_object_move(textblock, 0, 0); evas_object_resize(textblock, 320, 320); evas_object_resize(win, 320, 320); evas_object_show(win); elm_run(); return 0; } ELM_MAIN() ``` Reviewers: woohyun, bowonryu, segfaultxavi, cedric, bu5hm4n Reviewed By: woohyun, cedric Subscribers: raster, bu5hm4n, subodh6129, herdsman, segfaultxavi, zmike, cedric, #committers, #reviewers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9053 --- src/bin/elementary/test.c | 2 + src/bin/elementary/test_label.c | 24 ++ src/lib/evas/common/evas_font.h | 53 ++++- src/lib/evas/common/evas_font_load.c | 6 +- src/lib/evas/common/evas_font_main.c | 296 +++++++++++++++++++++--- src/lib/evas/common/evas_font_query.c | 55 +++-- src/lib/evas/common/evas_text_utils.c | 6 +- src/tests/evas/evas_buffer_helper.c | 199 ++++++++++++++++ src/tests/evas/evas_buffer_helper.h | 32 +++ src/tests/evas/evas_test_object_smart.c | 2 +- src/tests/evas/evas_test_textblock.c | 3 +- src/tests/evas/meson.build | 1 + 12 files changed, 629 insertions(+), 50 deletions(-) create mode 100644 src/tests/evas/evas_buffer_helper.c create mode 100644 src/tests/evas/evas_buffer_helper.h diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index 966d164c24..4150ab20ea 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -244,6 +244,7 @@ void test_textblock_fit(void *data, Evas_Object *obj, void *event_info); void test_label_ellipsis(void *data, Evas_Object *obj, void *event_info); void test_label_colors(void *data, Evas_Object *obj, void *event_info); void test_label_emoji(void *data, Evas_Object *obj, void *event_info); +void test_label_variation_sequence(void *data, Evas_Object *obj, void *event_info); void test_conformant(void *data, Evas_Object *obj, void *event_info); void test_conformant2(void *data, Evas_Object *obj, void *event_info); void test_conformant_indicator(void *data, Evas_Object *obj, void *event_info); @@ -1211,6 +1212,7 @@ add_tests: ADD_TEST(NULL, "Text", "Label Ellipsis", test_label_ellipsis); ADD_TEST(NULL, "Text", "Label Colors", test_label_colors); ADD_TEST(NULL, "Text", "Label Emoji", test_label_emoji); + ADD_TEST(NULL, "Text", "Label Variation Sequnece", test_label_variation_sequence); ADD_TEST_EO(NULL, "Text", "Efl.Ui.Textpath", test_ui_textpath); ADD_TEST_EO(NULL, "Text", "Efl.Canvas.Textblock style", test_canvas_textblock); diff --git a/src/bin/elementary/test_label.c b/src/bin/elementary/test_label.c index 929d06b1d5..5b3e5e8265 100644 --- a/src/bin/elementary/test_label.c +++ b/src/bin/elementary/test_label.c @@ -635,6 +635,30 @@ test_label_colors(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *ev evas_object_show(win); } +/*** Label variation sequence **************************************************************/ +void +test_label_variation_sequence(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Evas_Object *win, *lb; + + win = elm_win_util_standard_add("label-variation sequence", "Label variation sequnece"); + elm_win_autodel_set(win, EINA_TRUE); + + lb = elm_label_add(win); + elm_object_text_set(lb, + "You need to have at least on font contains variation sequence
" + "Three different 8 glyphs :
" + "88️8️⃣
" + "line with 3 variation glyphs :
" + "8️⃣☪️AAA☪︎1234567️⃣" + ); + evas_object_size_hint_weight_set(lb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, lb); + evas_object_show(lb); + + evas_object_show(win); +} + /*** Label Emoji *************************************************************/ static char * _fontlist_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED) diff --git a/src/lib/evas/common/evas_font.h b/src/lib/evas/common/evas_font.h index 4588a31f0d..e6f2bdebd5 100644 --- a/src/lib/evas/common/evas_font.h +++ b/src/lib/evas/common/evas_font.h @@ -56,7 +56,32 @@ typedef unsigned long long DATA64; #define LKU(x) eina_lock_release(&(x)) #define LKDBG(x) eina_lock_debug(&(x)) +/** + * See explanation of variation_sequences at: + * https://unicode.org/Public/UCD/latest/ucd/StandardizedVariants.txt + * https://unicode.org/reports/tr37/ + * https://unicode.org/ivd/ + * https://www.freetype.org/freetype2/docs/reference/ft2-glyph_variants.html +*/ +#define VAR_SEQ(x) GENERIC_VARIATION_SEQUENCES(x) | IDEOGRAPHICS_VARIATION_SEQUENCES(x) | MANGOLIAN_VARIATION_SEQUENCES(x) +#define GENERIC_VARIATION_SEQUENCES(x) (x>=0xFE00 && x<=0xFE0F) ? x : 0 +#define IDEOGRAPHICS_VARIATION_SEQUENCES(x) (x>=0xE0100 && x<=0xE01EF) ? x : 0 +#define MANGOLIAN_VARIATION_SEQUENCES(x) (x>=0x180B && x<=0x180D) ? x : 0 +/** + * http://unicode.org/emoji/charts/emoji-variants.html +*/ +#define VARIATION_EMOJI_PRESENTATION 0xFE0F +#define VARIATION_TEXT_PRESENTATION 0xFE0E +/** + * These Options (Flags) are used with evas_common_font_glyph_search function + */ +#define EVAS_FONT_SEARCH_OPTION_NONE 0x0000 +#define EVAS_FONT_SEARCH_OPTION_SKIP_COLOR 0x0001 + + +#define FASH_INT_MAGIC 0x01012345 +#define FASH_GLYPH_MAGIC 0x02012345 enum _Evas_Font_Style { @@ -128,6 +153,10 @@ typedef struct _RGBA_Font_Source RGBA_Font_Source; typedef struct _RGBA_Font_Glyph RGBA_Font_Glyph; typedef struct _RGBA_Font_Glyph_Out RGBA_Font_Glyph_Out; +typedef struct _Fash_Item_variation_Index_Item Fash_Item_variation_Index_Item; +typedef struct _Fash_Item_variation_List Fash_Item_variation_List; +typedef struct _Fash_Item_Index_Map_Variations Fash_Item_Index_Map_Variations; + typedef struct _Fash_Item_Index_Map Fash_Item_Index_Map; typedef struct _Fash_Int_Map Fash_Int_Map; typedef struct _Fash_Int_Map2 Fash_Int_Map2; @@ -139,9 +168,25 @@ struct _Fash_Item_Index_Map RGBA_Font_Int *fint; int index; }; +struct _Fash_Item_variation_Index_Item +{ + Fash_Item_Index_Map item; + Eina_Unicode variation_sequence; +}; +struct _Fash_Item_variation_List +{ + Fash_Item_variation_Index_Item *list; + size_t length; + size_t capacity; +}; +struct _Fash_Item_Index_Map_Variations +{ + Fash_Item_Index_Map item; + Fash_Item_variation_List *variations; +}; struct _Fash_Int_Map { - Fash_Item_Index_Map item[256]; + Fash_Item_Index_Map_Variations items[256]; }; struct _Fash_Int_Map2 { @@ -149,6 +194,7 @@ struct _Fash_Int_Map2 }; struct _Fash_Int { + unsigned int MAGIC; Fash_Int_Map2 *bucket[256]; void (*freeme) (Fash_Int *fash); }; @@ -166,6 +212,7 @@ struct _Fash_Glyph_Map2 }; struct _Fash_Glyph { + unsigned int MAGIC; Fash_Glyph_Map2 *bucket[256]; void (*freeme) (Fash_Glyph *fash); }; @@ -347,7 +394,7 @@ void *evas_common_font_freetype_face_get(RGBA_Font *font); /* XXX: Not EAPI on p EAPI RGBA_Font_Glyph *evas_common_font_int_cache_glyph_get (RGBA_Font_Int *fi, FT_UInt index); EAPI Eina_Bool evas_common_font_int_cache_glyph_render(RGBA_Font_Glyph *fg); -EAPI FT_UInt evas_common_get_char_index (RGBA_Font_Int* fi, Eina_Unicode gl); +EAPI FT_UInt evas_common_get_char_index (RGBA_Font_Int* fi, Eina_Unicode gl, Eina_Unicode variation_sequence); /* load */ EAPI void evas_common_font_dpi_set (int dpi_h, int dpi_v); @@ -397,7 +444,7 @@ EAPI void evas_common_font_ascent_descent_get(RGBA_Font *fn, const EAPI void *evas_common_font_glyph_compress(void *data, int num_grays, int pixel_mode, int pitch_data, int w, int h, int *size_ret); EAPI DATA8 *evas_common_font_glyph_uncompress(RGBA_Font_Glyph *fg, int *wret, int *hret); -EAPI int evas_common_font_glyph_search (RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl); +EAPI int evas_common_font_glyph_search (RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl, Eina_Unicode variation_sequence, uint32_t evas_font_search_options); void evas_common_font_load_init(void); void evas_common_font_load_shutdown(void); diff --git a/src/lib/evas/common/evas_font_load.c b/src/lib/evas/common/evas_font_load.c index f2011bd72f..3aa2070499 100644 --- a/src/lib/evas/common/evas_font_load.c +++ b/src/lib/evas/common/evas_font_load.c @@ -687,7 +687,11 @@ evas_common_font_free(RGBA_Font *fn) evas_common_font_int_unref(fi); evas_common_font_flush(); eina_list_free(fn->fonts); - if (fn->fash) fn->fash->freeme(fn->fash); + if (fn->fash) + { + fn->fash->freeme(fn->fash); + fn->fash = NULL; + } LKD(fn->lock); free(fn); } diff --git a/src/lib/evas/common/evas_font_main.c b/src/lib/evas/common/evas_font_main.c index 1a163748ca..c41e350dcf 100644 --- a/src/lib/evas/common/evas_font_main.c +++ b/src/lib/evas/common/evas_font_main.c @@ -355,33 +355,182 @@ end: /* Set of common functions that are used in a couple of places. */ static void -_fash_int2_free(Fash_Int_Map2 *fash) -{ +_fash_int_map_and_variations_free(Fash_Int_Map *map) + { + if(!map) + return; int i; - for (i = 0; i < 256; i++) if (fash->bucket[i]) free(fash->bucket[i]); - free(fash); + for (i = 0; i < 256; i++) + { + if (map->items[i].variations) + { + if (map->items[i].variations->list) + { + free(map->items[i].variations->list); + map->items[i].variations->list = NULL; + map->items[i].variations->capacity = 0; + map->items[i].variations->length = 0; + } + free(map->items[i].variations); + map->items[i].variations = NULL; + } + } + + free(map); +} + +static void +_fash_int2_free(Fash_Int_Map2 *fash) + { + int i; + if (fash) + { + for (i = 0; i < 256; i++) + if (fash->bucket[i]) + { + _fash_int_map_and_variations_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + free(fash); + fash = NULL; + } } static void _fash_int_free(Fash_Int *fash) { int i; + if (fash) + { + if (fash->MAGIC != FASH_INT_MAGIC) + { + return; + } - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_int2_free(fash->bucket[i]); - free(fash); + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_int2_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } + free(fash); + } } static Fash_Int * _fash_int_new(void) { Fash_Int *fash = calloc(1, sizeof(Fash_Int)); + EINA_SAFETY_ON_NULL_RETURN_VAL(fash, NULL); + fash->MAGIC = FASH_INT_MAGIC; fash->freeme = _fash_int_free; return fash; } +static Fash_Item_variation_List * +_variations_list_new(void) +{ + Fash_Item_variation_List *variations = calloc(1, sizeof(Fash_Item_variation_List)); + EINA_SAFETY_ON_NULL_RETURN_VAL(variations, NULL); + variations->capacity = 0; + variations->length = 0; + variations->list = 0; + return variations; +} + +static void +_variations_list_add(Fash_Item_variation_List *variations,RGBA_Font_Int *fint, int index, Eina_Unicode variation_sequence) +{ + Fash_Item_variation_Index_Item *list = variations->list; + if (variations->capacity == variations->length) + { + list = (Fash_Item_variation_Index_Item *) realloc(list, (variations->capacity + 4) * sizeof(Fash_Item_variation_Index_Item)); + if (list) + { + variations->list = list; + variations->capacity += 4; + } + } + + EINA_SAFETY_ON_NULL_RETURN(list); + + int start = 0; + int end = variations->length; + if (end == 0) + { + // if only on element just add it in 0 index + variations->list[0].item.fint = fint; + variations->list[0].item.index = index; + variations->list[0].variation_sequence = variation_sequence; + variations->length++; + } + else + { + // find lower bound + while (end > start) + { + int middle = start + (end - start) / 2; + if (variations->list[middle].variation_sequence >= variation_sequence) + end = middle; + else + start = middle + 1; + } + + // if passed value founded in list, just replace it + if (start < (int)variations->length && variations->list[start].variation_sequence == variation_sequence) + { + variations->list[start].item.fint = fint; + variations->list[start].item.index = index; + variations->list[start].variation_sequence = variation_sequence; + return; + } + + // shift array to insert item + for (int i = (variations->length - 1) ; i >= start; i--) + { + variations->list[i + 1] = variations->list[i]; + } + + // insert new item and keep array sorted + variations->list[start].item.fint = fint; + variations->list[start].item.index = index; + variations->list[start].variation_sequence = variation_sequence; + variations->length++; + } +} + + static Fash_Item_Index_Map * -_fash_int_find(Fash_Int *fash, int item) +_variations_list_find(Fash_Item_variation_List * variations, Eina_Unicode variation_sequence) +{ + if (!variations) + return NULL; + + if (!variations->list) + return NULL; + + int start = 0; + int end = variations->length; + + while(end > start) + { + int middle = start + (end - start) / 2; + if (variations->list[middle].variation_sequence == variation_sequence) + return &(variations->list[middle].item); + else if (variations->list[middle].variation_sequence < variation_sequence) + start = middle + 1; + else + end = middle - 1; + } + + return NULL; +} + +static const Fash_Item_Index_Map * +_fash_int_find(Fash_Int *fash, int item, Eina_Unicode variation_sequence) { int grp, maj, min; @@ -391,14 +540,22 @@ _fash_int_find(Fash_Int *fash, int item) min = item & 0xff; if (!fash->bucket[grp]) return NULL; if (!fash->bucket[grp]->bucket[maj]) return NULL; - return &(fash->bucket[grp]->bucket[maj]->item[min]); + if (!variation_sequence) + return &(fash->bucket[grp]->bucket[maj]->items[min].item); + else + return _variations_list_find(fash->bucket[grp]->bucket[maj]->items[min].variations, variation_sequence); } static void -_fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx) +_fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx, Eina_Unicode variation_sequence) { int grp, maj, min; + // If we already have cached passed item, skip adding it again + const Fash_Item_Index_Map *fm = _fash_int_find(fash, item, variation_sequence); + if (fm && fm->fint) + return; + // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) grp = (item >> 16) & 0xff; maj = (item >> 8) & 0xff; @@ -409,8 +566,20 @@ _fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx) if (!fash->bucket[grp]->bucket[maj]) fash->bucket[grp]->bucket[maj] = calloc(1, sizeof(Fash_Int_Map)); EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]); - fash->bucket[grp]->bucket[maj]->item[min].fint = fint; - fash->bucket[grp]->bucket[maj]->item[min].index = idx; + if (variation_sequence) + { + if (!fash->bucket[grp]->bucket[maj]->items[min].variations) + { + fash->bucket[grp]->bucket[maj]->items[min].variations =_variations_list_new(); + EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]->items[min].variations); + } + _variations_list_add(fash->bucket[grp]->bucket[maj]->items[min].variations, fint, idx, variation_sequence); + } + else + { + fash->bucket[grp]->bucket[maj]->items[min].item.fint = fint; + fash->bucket[grp]->bucket[maj]->items[min].item.index = idx; + } } static void @@ -462,24 +631,45 @@ _fash_gl2_free(Fash_Glyph_Map2 *fash) int i; // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_glyph_free(fash->bucket[i]); + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_glyph_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } free(fash); } static void _fash_gl_free(Fash_Glyph *fash) { - int i; + if (fash) + { + if (fash->MAGIC != FASH_GLYPH_MAGIC) + return; - // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) - for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_gl2_free(fash->bucket[i]); - free(fash); + int i; + // 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16) + for (i = 0; i < 256; i++) + { + if (fash->bucket[i]) + { + _fash_gl2_free(fash->bucket[i]); + fash->bucket[i] = NULL; + } + } + free(fash); + } } static Fash_Glyph * _fash_gl_new(void) { Fash_Glyph *fash = calloc(1, sizeof(Fash_Glyph)); + EINA_SAFETY_ON_NULL_RETURN_VAL(fash, NULL); + fash->MAGIC = FASH_GLYPH_MAGIC; fash->freeme = _fash_gl_free; return fash; } @@ -680,7 +870,7 @@ struct _Font_Char_Index }; EAPI FT_UInt -evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) +evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl, Eina_Unicode variation_sequence) { static const unsigned short mapfix[] = { @@ -742,7 +932,10 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) * that something else try to use it. */ /* FTLOCK(); */ - result.index = FT_Get_Char_Index(fi->src->ft.face, gl); + if (variation_sequence) + result.index = FT_Face_GetCharVariantIndex(fi->src->ft.face, gl, variation_sequence); + else + result.index = FT_Get_Char_Index(fi->src->ft.face, gl); /* FTUNLOCK(); */ result.gl = gl; @@ -774,7 +967,10 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) { gl = mapfix[(i << 1) + 1]; FTLOCK(); - result.index = FT_Get_Char_Index(fi->src->ft.face, gl); + if (variation_sequence) + result.index = FT_Face_GetCharVariantIndex(fi->src->ft.face, gl, variation_sequence); + else + result.index = FT_Get_Char_Index(fi->src->ft.face, gl); FTUNLOCK(); break; } @@ -799,20 +995,49 @@ evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl) return result.index; } + +/* + * @internal + * Search for unicode glyph inside all font files, and return font and glyph index + * + * @param[in] fn the font to use. + * @param[out] fi_ret founded font. + * @param[in] gl unicode glyph to search for + * @param[in] variation_sequence for the gl glyph + * @param[in] evas_font_search_options search options when searching font files + * + */ + EAPI int -evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl) +evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl, Eina_Unicode variation_sequence, uint32_t evas_font_search_options) { Eina_List *l; if (fn->fash) { - Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl); + const Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl, variation_sequence); if (fm) { if (fm->fint) { - *fi_ret = fm->fint; - return fm->index; + if (evas_font_search_options == EVAS_FONT_SEARCH_OPTION_NONE) + { + *fi_ret = fm->fint; + return fm->index; + } + else if( (evas_font_search_options & EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) == EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) + { + if (!fm->fint->src->ft.face) + { + evas_common_font_int_reload(fm->fint); + } + + if (fm->fint->src->ft.face && !FT_HAS_COLOR(fm->fint->src->ft.face)) + { + *fi_ret = fm->fint; + return fm->index; + } + } } else if (fm->index == -1) return 0; } @@ -851,20 +1076,35 @@ evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicod } if (fi->src->ft.face) { - idx = evas_common_get_char_index(fi, gl); + Eina_Bool is_color_only = (evas_font_search_options & EVAS_FONT_SEARCH_OPTION_SKIP_COLOR) == EVAS_FONT_SEARCH_OPTION_SKIP_COLOR && + FT_HAS_COLOR(fi->src->ft.face); + + if (is_color_only) + { + /* This is color font ignore it */ + continue; + } + + idx = (int) evas_common_get_char_index(fi, gl, variation_sequence); if (idx != 0) { if (!fi->ft.size) evas_common_font_int_load_complete(fi); - if (!fn->fash) fn->fash = _fash_int_new(); - if (fn->fash) _fash_int_add(fn->fash, gl, fi, idx); + if (!is_color_only) + { + if (!fn->fash) fn->fash = _fash_int_new(); + if (fn->fash) _fash_int_add(fn->fash, gl, fi, idx, variation_sequence); + } *fi_ret = fi; return idx; } else { - if (!fn->fash) fn->fash = _fash_int_new(); - if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1); + if (!is_color_only) + { + if (!fn->fash) fn->fash = _fash_int_new(); + if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1, variation_sequence); + } } } } diff --git a/src/lib/evas/common/evas_font_query.c b/src/lib/evas/common/evas_font_query.c index 1ff0447963..bab7376621 100644 --- a/src/lib/evas/common/evas_font_query.c +++ b/src/lib/evas/common/evas_font_query.c @@ -1,5 +1,6 @@ #include "evas_font_private.h" /* for Frame-Queuing support */ +#define VAR_SEQ_SAFE(x) (((x) 0x1F) && - evas_common_font_glyph_search(fn, &fi, *base_char)) + evas_common_font_glyph_search(fn, &fi, *base_char, 0, EVAS_FONT_SEARCH_OPTION_NONE)) break; base_char++; } @@ -49,7 +53,8 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If everything else fails, at least try to find a font for the * replacement char */ if (base_char == run_end) - evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR); + evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR, 0, EVAS_FONT_SEARCH_OPTION_NONE); +get_top_font: if (!fi) fi = fn->fonts->data; @@ -80,15 +85,17 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi if (evas_common_language_char_script_get(*itr) == EVAS_SCRIPT_INHERITED) continue; + Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1); + /* Break if either it's not in the font, or if it is in the * script's font. */ - if (!evas_common_get_char_index(fi, *itr)) - break; + if (!evas_common_get_char_index(fi, *itr, variation_sequence)) + break; if (fi != *script_fi) { - if (evas_common_get_char_index(*script_fi, *itr)) - break; + if (evas_common_get_char_index(*script_fi, *itr, variation_sequence)) + break; } } @@ -102,10 +109,25 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If we can find a font, use it. Otherwise, find the first * char the run of chars that can't be rendered until the first * one that can. */ - if (evas_common_font_glyph_search(fn, &tmp_fi, *itr)) + Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1); + if (evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE)) { fi = tmp_fi; } + else if ( (variation_sequence == VARIATION_TEXT_PRESENTATION) && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_SKIP_COLOR)) + { + /* If we can not find unicode with variation sequence, then we will + Search and find for non color glyph because variation sequence is Text + */ + fi = tmp_fi; + } + else if ( variation_sequence && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE)) + { + /* If we can not find unicode with variation sequence, then we will + Search and find glyph without the variation sequence + */ + fi = tmp_fi; + } else { itr++; @@ -113,9 +135,13 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi * font */ for ( ; itr < run_end ; itr++) { + if(VAR_SEQ_SAFE(itr)) + continue; + + Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1); tmp_fi = fi; - if (evas_common_get_char_index(fi, *itr) || - evas_common_font_glyph_search(fn, &tmp_fi, *itr)) + if (evas_common_get_char_index(fi, *itr, variation_sequence) || + evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE)) { fi = tmp_fi; break; @@ -127,9 +153,9 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi * find a font most suitable for the replacement char and * break */ if ((itr == run_end) || - !evas_common_get_char_index(fi, REPLACEMENT_CHAR)) + !evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0)) { - evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR); + evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR, 0, EVAS_FONT_SEARCH_OPTION_NONE); break; } } @@ -140,8 +166,11 @@ evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi /* If this char is not renderable by any font, but the replacement * char can be rendered using the currentfont, continue this * run. */ - if (!evas_common_font_glyph_search(fn, &tmp_fi, *itr) && - evas_common_get_char_index(fi, REPLACEMENT_CHAR)) + Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1); + + if (!evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE) && + (variation_sequence ? !evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE) : 1) && + evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0)) { itr++; } diff --git a/src/lib/evas/common/evas_text_utils.c b/src/lib/evas/common/evas_text_utils.c index e9c5cc2c94..acf7cf8fb7 100644 --- a/src/lib/evas/common/evas_text_utils.c +++ b/src/lib/evas/common/evas_text_utils.c @@ -1066,7 +1066,7 @@ _content_create_ot(RGBA_Font_Int *fi, const Eina_Unicode *text, /* If we got a malformed index, show the replacement char instead */ if (gl_itr->index == 0) { - gl_itr->index = evas_common_get_char_index(fi, REPLACEMENT_CHAR); + gl_itr->index = evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0); is_replacement = EINA_TRUE; } idx = gl_itr->index; @@ -1172,10 +1172,10 @@ _content_create_regular(RGBA_Font_Int *fi, const Eina_Unicode *text, _gl = *text; if (_gl == 0) break; - idx = evas_common_get_char_index(fi, _gl); + idx = evas_common_get_char_index(fi, _gl, 0); if (idx == 0) { - idx = evas_common_get_char_index(fi, REPLACEMENT_CHAR); + idx = evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0); } fg = evas_common_font_int_cache_glyph_get(fi, idx); diff --git a/src/tests/evas/evas_buffer_helper.c b/src/tests/evas/evas_buffer_helper.c new file mode 100644 index 0000000000..db705df75e --- /dev/null +++ b/src/tests/evas/evas_buffer_helper.c @@ -0,0 +1,199 @@ + +#include +#include +#include +#include + +/* Convenience routine to allocate and initialize the canvas. + * In a real application we'd be using ecore_evas_buffer_new() instead. + */ +Evas *canvas_create(int width, int height) +{ + Evas *canvas; + Evas_Engine_Info_Buffer *einfo; + int method; + void *pixels; + + /* Request a handle for the 'buffer' type of rendering engine. */ + method = evas_render_method_lookup("buffer"); + if (method <= 0) + { + fputs("ERROR: evas was not compiled with 'buffer' engine!\n", stderr); + return NULL; + } + + /* Create a general canvas object. + * Note that we are responsible for freeing the canvas when we're done. */ + canvas = evas_new(); + if (!canvas) + { + fputs("ERROR: could not instantiate new evas canvas.\n", stderr); + return NULL; + } + + /* Specify that the canvas will be rendering using the buffer engine method. + * We also size the canvas and viewport to the same width and height, with + * the viewport set to the origin of the canvas. + */ + evas_output_method_set(canvas, method); + evas_output_size_set(canvas, width, height); + evas_output_viewport_set(canvas, 0, 0, width, height); + + /* Before we can use the engine, we *must* set its configuration + * parameters. The available parameters are kept in a struct + * named Evas_Engine_Info which is internal to Evas. Thus to set + * parameters we must first request the current info object from + * our canvas: + */ + einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas); + if (!einfo) + { + fputs("ERROR: could not get evas engine info!\n", stderr); + evas_free(canvas); + return NULL; + } + + /* Create the underlying data buffer that our canvas will use. This + * is a simple array of ARGB32 pixels. Each color component + * (including alpha) is one byte, resulting in 4 bytes per pixel (or + * 32 bits). We can thus store each pixel in an integer data type, + * thus calculating our data buffer as W x H x sizeof(int) bytes in + * length. + */ + pixels = malloc(width * height * sizeof(int)); + if (!pixels) + { + fputs("ERROR: could not allocate canvas pixels!\n", stderr); + evas_free(canvas); + return NULL; + } + + /* Next set the various configuration parameters. We + * register the pixel buffer that the canvas will use, + * indicate the pixel format as ARGB32, and the size of + * each row of data. */ + einfo->info.depth_type = EVAS_ENGINE_BUFFER_DEPTH_ARGB32; + einfo->info.dest_buffer = pixels; + einfo->info.dest_buffer_row_bytes = width * sizeof(int); + einfo->info.use_color_key = 0; + einfo->info.alpha_threshold = 0; + einfo->info.func.new_update_region = NULL; + einfo->info.func.free_update_region = NULL; + + /* Finally, we configure the canvas with our chosen parameters. */ + evas_engine_info_set(canvas, (Evas_Engine_Info *)einfo); + + return canvas; +} + +/* Convenience routine to shut down the canvas. + * In a real application we'd be using ecore_evas_free() instead + */ +void canvas_destroy(Evas *canvas) +{ + Evas_Engine_Info_Buffer *einfo; + + einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas); + if (!einfo) + { + fputs("ERROR: could not get evas engine info!\n", stderr); + evas_free(canvas); + return; + } + + /* Free the data buffer we allocated in create_buffer() */ + free(einfo->info.dest_buffer); + + /* Finally, free the canvas itself. */ + evas_free(canvas); +} + +/* + * return internal buffer address +*/ +void* canvas_buffer(Evas *canvas) +{ + Evas_Engine_Info_Buffer *einfo; + einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas); + if (!einfo) + { + fputs("ERROR: could not get evas engine info!\n", stderr); + return NULL; + } + return einfo->info.dest_buffer; +} + +/* Convenience routine to update the scene. + * In a real application Ecore Evas would be doing this for us. + */ +void canvas_draw(Evas *canvas) +{ + Eina_List *updates, *n; + Eina_Rectangle *update; + + /* Render the canvas, and get a list of the updated rectangles. */ + updates = evas_render_updates(canvas); + + /* Just for informative purposes, print out the areas being updated: */ + EINA_LIST_FOREACH(updates, n, update) + printf("UPDATED REGION: pos: %3d, %3d size: %3dx%3d\n", + update->x, update->y, update->w, update->h); + + /* Free the list of update rectangles */ + evas_render_updates_free(updates); +} + +/* Output the canvas buffer to a Portable Pixel Map (PPM) file */ +void canvas_save(Evas *canvas, const char *dest) +{ + Evas_Engine_Info_Buffer *einfo; + const unsigned int *pixels, *pixels_end; + int width, height; + FILE *f; + + /* Retrieve the current data buffer. */ + einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas); + if (!einfo) + { + fputs("ERROR: could not get evas engine info!\n", stderr); + return; + } + + /* Retrieve the canvas dimensions */ + evas_output_size_get(canvas, &width, &height); + + /* Open our output PPM file for writing */ + f = fopen(dest, "wb+"); + if (!f) + { + fprintf(stderr, "ERROR: could not open for writing '%s': %s\n", + dest, strerror(errno)); + return; + } + + /* Write out the pixel data to the PPM file */ + pixels = einfo->info.dest_buffer; + pixels_end = pixels + (width * height); + + /* PPM P6 format is dead simple to write. First we output a magic + * number 'P6' to designate the file as PPM, then the width and + * height on their own line in ASCII decimal, followed by the maximum + * color value (255) on its own line in ASCII decimal, and finally a + * the pixel data in RGB order with each color component written as + * a char (byte). No alpha information is stored. + */ + fprintf(f, "P6\n%d %d\n255\n", width, height); + for (; pixels < pixels_end; pixels++) + { + int r, g, b; + + r = ((*pixels) & 0xff0000) >> 16; + g = ((*pixels) & 0x00ff00) >> 8; + b = (*pixels) & 0x0000ff; + + fprintf(f, "%c%c%c", r, g, b); + } + + fclose(f); + printf("saved scene as '%s'\n", dest); +} \ No newline at end of file diff --git a/src/tests/evas/evas_buffer_helper.h b/src/tests/evas/evas_buffer_helper.h new file mode 100644 index 0000000000..9b55c17ca3 --- /dev/null +++ b/src/tests/evas/evas_buffer_helper.h @@ -0,0 +1,32 @@ +#ifndef _EVAS_BUFFER_HELPER_H_ +#define _EVAS_BUFFER_HELPER_H_ + +#include + +/* + * Create canvas with buffer and create its internal buffer +*/ +Evas* canvas_create(int width, int height); + +/* + * return internal buffer +*/ +void* canvas_buffer(Evas *evas); + +/* + * Destroy canvas and internal buffer +*/ +void canvas_destroy(Evas *canvas); + +/* + * Save canvas into image with PPM P6 format (*.ppm) +*/ +void canvas_save(Evas *canvas, const char *dest); + +/* + * Draw canvas (this should be called after making changes into evas) +*/ +void canvas_draw(Evas *canvas); + + +#endif // !_EVAS_BUFFER_HELPER_H_ diff --git a/src/tests/evas/evas_test_object_smart.c b/src/tests/evas/evas_test_object_smart.c index 188e9c244b..e209100de6 100644 --- a/src/tests/evas/evas_test_object_smart.c +++ b/src/tests/evas/evas_test_object_smart.c @@ -1,6 +1,6 @@ /* * TODO: - * * Test different font lodaing mechanisms. + * * Test different font loading mechanisms. */ #ifdef HAVE_CONFIG_H diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index a450ebc608..1a75153dd9 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -1,6 +1,6 @@ /* * TODO: - * * Test different font lodaing mechanisms. + * * Test different font loading mechanisms. */ #ifdef HAVE_CONFIG_H @@ -15,6 +15,7 @@ #include "evas_suite.h" #include "evas_tests_helpers.h" +#include "evas_buffer_helper.h" #define TESTS_DIC_DIR TESTS_SRC_DIR"/dicts" diff --git a/src/tests/evas/meson.build b/src/tests/evas/meson.build index 0e92eba83e..d89d602507 100644 --- a/src/tests/evas/meson.build +++ b/src/tests/evas/meson.build @@ -5,6 +5,7 @@ evas_suite_src = [ 'evas_test_object.c', 'evas_test_object_smart.c', 'evas_test_textblock.c', + 'evas_buffer_helper.c', 'evas_test_text.c', 'evas_test_callbacks.c', 'evas_test_render_engines.c',