Evas textblock: Fixed async rendering support.
[framework/uifw/evas.git] / src / lib / engines / common / evas_font_ot.c
1 #include "evas_font_ot.h"
2
3 #ifdef OT_SUPPORT
4 # include <hb.h>
5 # include <hb-ft.h>
6 #endif
7
8 #include "evas_common.h"
9
10 #include <Eina.h>
11 #include "evas_font_private.h"
12
13 EAPI Eina_Bool
14 evas_common_font_ot_is_enabled(void)
15 {
16 #ifdef OT_SUPPORT
17    static int ret = -1;
18    const char *env;
19    if (ret != -1)
20      {
21         return ret;
22      }
23
24    env = getenv("EVAS_USE_OT");
25    if (env && atoi(env))
26      {
27         ret = EINA_TRUE;
28         return ret;
29      }
30 #endif
31    return EINA_FALSE;
32 }
33
34 #ifdef OT_SUPPORT
35 /* FIXME: doc. returns #items */
36 EAPI int
37 evas_common_font_ot_cluster_size_get(const Evas_Text_Props *props, size_t char_index, int orig_len)
38 {
39    int i;
40    int items;
41    int left_bound, right_bound;
42    size_t base_cluster = EVAS_FONT_OT_POS_GET(props->ot_data->items[char_index]);
43    for (i = (int) char_index ;
44          (i >= 0) &&
45          (EVAS_FONT_OT_POS_GET(props->ot_data->items[i]) == base_cluster) ;
46          i--)
47      ;
48    left_bound = i;
49    for (i = (int) char_index + 1;
50          (i < (int) props->ot_data->len) &&
51          (EVAS_FONT_OT_POS_GET(props->ot_data->items[i]) == base_cluster) ;
52          i++)
53      ;
54    right_bound = i;
55    if (right_bound == left_bound)
56      {
57         items = 1;
58      }
59    else if (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
60      {
61         if (left_bound < 0)
62           {
63              items = orig_len -
64                 props->ot_data->items[left_bound + 1].source_cluster;
65           }
66         else
67           {
68              items = props->ot_data->items[left_bound].source_cluster -
69                 props->ot_data->items[left_bound + 1].source_cluster;
70           }
71      }
72    else
73      {
74         if (right_bound == (int) props->ot_data->len)
75           {
76              items = orig_len -
77                 props->ot_data->items[right_bound - 1].source_cluster;
78           }
79         else
80           {
81              items = props->ot_data->items[right_bound].source_cluster -
82                 props->ot_data->items[right_bound - 1].source_cluster;
83           }
84      }
85    return (items > 0) ? items : 1;
86 }
87
88 EAPI void
89 evas_common_font_ot_load_face(void *_font)
90 {
91    RGBA_Font_Source *font = (RGBA_Font_Source *) _font;
92    font->hb.face = hb_ft_face_create(font->ft.face, NULL);
93 }
94
95 EAPI void
96 evas_common_font_ot_unload_face(void *_font)
97 {
98    RGBA_Font_Source *font = (RGBA_Font_Source *) _font;
99    if (!font->hb.face) return;
100    hb_face_destroy(font->hb.face);
101    font->hb.face = NULL;
102 }
103
104 static void
105 _evas_common_font_ot_shape(hb_buffer_t *buffer, RGBA_Font_Source *src)
106 {
107    hb_font_t   *hb_font;
108
109    hb_font = hb_ft_font_create(src->ft.face, NULL);
110
111    hb_shape(hb_font, src->hb.face, buffer, NULL, 0);
112    hb_font_destroy(hb_font);
113 }
114
115 /* Won't work in the middle of ligatures */
116 EAPI void
117 evas_common_font_ot_cutoff_text_props(Evas_Text_Props *props, int cutoff)
118 {
119    Evas_Font_OT_Data *new_data;
120    if ((cutoff <= 0) || (!props->ot_data) ||
121          (((size_t) cutoff) >= props->ot_data->len))
122      return;
123
124    new_data = malloc(sizeof(Evas_Font_OT_Data));
125    memcpy(new_data, props->ot_data, sizeof(Evas_Font_OT_Data));
126    new_data->refcount = 1;
127    new_data->len = cutoff;
128    new_data->items = malloc(cutoff * sizeof(Evas_Font_OT_Data_Item));
129
130    if (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
131      {
132         memcpy(new_data->items,
133               props->ot_data->items + (props->ot_data->len - cutoff),
134               cutoff * sizeof(Evas_Font_OT_Data_Item));
135      }
136    else
137      {
138         memcpy(new_data->items,
139               props->ot_data->items,
140               cutoff * sizeof(Evas_Font_OT_Data_Item));
141      }
142
143    evas_common_font_ot_props_unref(props->ot_data);
144    props->ot_data = new_data;
145 }
146
147 /* Won't work in the middle of ligatures
148  * aissumes ext doesn't have an already init ot_data
149  * we assume there's at least one char in each part */
150 EAPI void
151 evas_common_font_ot_split_text_props(Evas_Text_Props *base,
152       Evas_Text_Props *ext, int cutoff)
153 {
154    Evas_Font_OT_Data *new_data;
155    int i;
156    if ((cutoff <= 0) || (!base->ot_data) ||
157          (((size_t) cutoff) >= base->ot_data->len))
158      return;
159
160    ext->ot_data = calloc(1, sizeof(Evas_Font_OT_Data));
161    ext->ot_data->refcount = 1;
162    ext->ot_data->len = base->ot_data->len - cutoff;
163    ext->ot_data->items = calloc(ext->ot_data->len,
164          sizeof(Evas_Font_OT_Data_Item));
165
166    new_data = malloc(sizeof(Evas_Font_OT_Data));
167    memcpy(new_data, base->ot_data, sizeof(Evas_Font_OT_Data));
168    new_data->refcount = 1;
169    new_data->items = malloc(cutoff * sizeof(Evas_Font_OT_Data_Item));
170    new_data->len = cutoff;
171
172    if (base->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
173      {
174         memcpy(ext->ot_data->items, base->ot_data->items,
175               ext->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
176         memcpy(new_data->items,
177               base->ot_data->items + ext->ot_data->len,
178               cutoff * sizeof(Evas_Font_OT_Data_Item));
179      }
180    else
181      {
182         memcpy(ext->ot_data->items, base->ot_data->items + cutoff,
183               ext->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
184         memcpy(new_data->items, base->ot_data->items,
185               cutoff * sizeof(Evas_Font_OT_Data_Item));
186      }
187    evas_common_font_ot_props_unref(base->ot_data);
188    base->ot_data = new_data;
189
190    /* Adjust the offset of the clusters */
191      {
192         size_t min;
193         min = ext->ot_data->items[0].source_cluster;
194         for (i = 1 ; i < (int) ext->ot_data->len ; i++)
195           {
196              if (ext->ot_data->items[i].source_cluster < min)
197                {
198                   min = ext->ot_data->items[i].source_cluster;
199                }
200           }
201         for (i = 0 ; i < (int) ext->ot_data->len ; i++)
202           {
203              ext->ot_data->items[i].source_cluster -= min;
204           }
205         ext->ot_data->offset = base->ot_data->offset + min;
206      }
207 }
208
209 /* Won't work in the middle of ligatures
210  * assumes both are init correctly and that both are from the
211  * same origin item, i.e both have the same script + direction.
212  * assume item1 is logically first */
213 EAPI void
214 evas_common_font_ot_merge_text_props(Evas_Text_Props *item1,
215       const Evas_Text_Props *item2)
216 {
217    Evas_Font_OT_Data *new_data;
218    Evas_Font_OT_Data_Item *itr; /* Itr will be used for adding back
219                                          the offsets */
220    size_t len;
221    if (!item1->ot_data || !item2->ot_data)
222      return;
223    len = item1->ot_data->len + item2->ot_data->len;
224
225    new_data = malloc(sizeof(Evas_Font_OT_Data));
226    memcpy(new_data, item1->ot_data, sizeof(Evas_Font_OT_Data));
227    new_data->refcount = 1;
228    new_data->items = malloc(len * sizeof(Evas_Font_OT_Data_Item));
229    new_data->len = len;
230    if (item1->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
231      {
232         memcpy(new_data->items, item2->ot_data->items,
233               item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
234         memcpy(new_data->items + item2->ot_data->len, item1->ot_data->items,
235               item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
236         itr = new_data->items;
237      }
238    else
239      {
240         memcpy(new_data->items, item1->ot_data->items,
241               item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
242         memcpy(new_data->items + item1->ot_data->len, item2->ot_data->items,
243               item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
244         itr = new_data->items + item1->ot_data->len;
245      }
246    evas_common_font_ot_props_unref(item1->ot_data);
247    item1->ot_data = new_data;
248    /* Add back the offset of item2 to the newly created */
249    if (item2->ot_data->offset > 0)
250      {
251         int i;
252         for (i = 0 ; i < (int) item2->ot_data->len ; i++, itr++)
253           {
254              /* This must be > 0, just because this is how it works */
255              itr->source_cluster += item2->ot_data->offset -
256                 item1->ot_data->offset;
257           }
258      }
259 }
260
261 EAPI Eina_Bool
262 evas_common_font_ot_populate_text_props(void *_fn, const Eina_Unicode *text,
263       Evas_Text_Props *props, int len)
264 {
265    RGBA_Font *fn = (RGBA_Font *) _fn;
266    RGBA_Font_Int *fi;
267    hb_buffer_t *buffer;
268    hb_glyph_position_t *positions;
269    hb_glyph_info_t *infos;
270    int slen;
271    unsigned int i;
272    if (!evas_common_font_ot_is_enabled()) return EINA_TRUE;
273    if (props->ot_data)
274      {
275         evas_common_font_ot_props_unref(props->ot_data);
276      }
277    props->ot_data = calloc(1, sizeof(Evas_Font_OT_Data));
278    props->ot_data->refcount = 1;
279
280    fi = fn->fonts->data;
281    /* Load the font needed for this script */
282      {
283         /* Skip common chars */
284         const Eina_Unicode *tmp;
285         for (tmp = text ;
286               *tmp &&
287               evas_common_language_char_script_get(*tmp) == EVAS_SCRIPT_COMMON ;
288               tmp++)
289           ;
290         if (!*tmp && (tmp > text)) tmp--;
291         evas_common_font_glyph_search(fn, &fi, *tmp);
292      }
293    evas_common_font_int_reload(fi);
294    if (fi->src->current_size != fi->size)
295      {
296         FTLOCK();
297         FT_Activate_Size(fi->ft.size);
298         FTUNLOCK();
299         fi->src->current_size = fi->size;
300      }
301
302    if (len < 0)
303      {
304         slen = eina_unicode_strlen(text);
305      }
306    else
307      {
308         slen = len;
309      }
310
311    buffer = hb_buffer_create(slen);
312    hb_buffer_set_unicode_funcs(buffer, evas_common_language_unicode_funcs_get());
313    hb_buffer_set_language(buffer, hb_language_from_string(
314             evas_common_language_from_locale_get()));
315    hb_buffer_set_script(buffer, props->script);
316    hb_buffer_set_direction(buffer,
317          (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL) ?
318          HB_DIRECTION_RTL : HB_DIRECTION_LTR);
319    /* FIXME: add run-time conversions if needed, which is very unlikely */
320    hb_buffer_add_utf32(buffer, (const uint32_t *) text, slen, 0, slen);
321
322    _evas_common_font_ot_shape(buffer, fi->src);
323
324    props->ot_data->len = hb_buffer_get_length(buffer);
325    props->ot_data->items = calloc(props->ot_data->len,
326          sizeof(Evas_Font_OT_Data_Item));
327    positions = hb_buffer_get_glyph_positions(buffer);
328    infos = hb_buffer_get_glyph_infos(buffer);
329    for (i = 0 ; i < props->ot_data->len ; i++)
330      {
331         props->ot_data->items[i].index = infos[i].codepoint;
332         props->ot_data->items[i].source_cluster = infos[i].cluster;
333         props->ot_data->items[i].x_advance = positions[i].x_advance;
334         props->ot_data->items[i].x_offset = positions[i].x_offset;
335         props->ot_data->items[i].y_offset = positions[i].y_offset;
336      }
337
338    hb_buffer_destroy(buffer);
339    evas_common_font_int_use_trim();
340
341    return EINA_FALSE;
342 }
343
344 EAPI void
345 evas_common_font_ot_props_ref(Evas_Font_OT_Data *data)
346 {
347    data->refcount++;
348 }
349
350 EAPI void
351 evas_common_font_ot_props_unref(Evas_Font_OT_Data *data)
352 {
353    OTLOCK();
354    if (--data->refcount == 0)
355      {
356         if (data->items)
357           free(data->items);
358         free(data);
359      }
360    OTUNLOCK();
361 }
362 #endif
363