big patch from Samsung SAIT (Advanced research group) for async multi-frame
[framework/uifw/evas.git] / src / lib / engines / common / evas_font_draw.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #include "evas_common.h"
6 #include "evas_private.h"
7 #include "evas_blend_private.h"
8
9 #include "evas_intl_utils.h" /*defines INTERNATIONAL_SUPPORT if possible */
10 #include "evas_font_private.h" /* for Frame-Queuing support */
11
12 #ifdef EVAS_FRAME_QUEUING
13 EAPI void
14 evas_common_font_draw_init(void)
15 {
16    LKI(lock_font_draw);
17    LKI(lock_fribidi);
18 }
19
20 EAPI void
21 evas_common_font_draw_finish(void)
22 {
23    LKD(lock_font_draw);
24    LKD(lock_fribidi);
25 }
26 #endif
27
28 static void
29 _fash_int_free(Fash_Int *fash)
30 {
31    int i;
32    
33    for (i = 0; i < 256; i++) if (fash->bucket[i]) free(fash->bucket[i]);
34    free(fash);
35 }
36
37 static Fash_Int *
38 _fash_int_new(void)
39 {
40    Fash_Int *fash = calloc(1, sizeof(Fash_Int));
41    fash->freeme = _fash_int_free;
42    return fash;
43 }
44
45 static Fash_Item_Index_Map *
46 _fash_int_find(Fash_Int *fash, int item)
47 {
48    int maj, min;
49
50    item &= 0xffff; // fixme: to do > 65k
51    maj = (item >> 8) & 0xff;
52    min = item & 0xff;
53    if (!fash->bucket[maj]) return NULL;
54    return &(fash->bucket[maj]->item[min]);
55 }
56
57 static void
58 _fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int index)
59 {
60    int maj, min;
61    
62    item &= 0xffff; // fixme: to do > 65k
63    maj = (item >> 8) & 0xff;
64    min = item & 0xff;
65    if (!fash->bucket[maj])
66      fash->bucket[maj] = calloc(1, sizeof(Fash_Int_Map));
67    fash->bucket[maj]->item[min].fint = fint;
68    fash->bucket[maj]->item[min].index = index;
69 }
70
71
72
73
74
75 static void
76 _fash_gl_free(Fash_Glyph *fash)
77 {
78    int i;
79    
80    for (i = 0; i < 256; i++) if (fash->bucket[i]) free(fash->bucket[i]);
81    free(fash);
82 }
83
84 static Fash_Glyph *
85 _fash_gl_new(void)
86 {
87    Fash_Glyph *fash = calloc(1, sizeof(Fash_Glyph));
88    fash->freeme = _fash_gl_free;
89    return fash;
90 }
91
92 static RGBA_Font_Glyph *
93 _fash_gl_find(Fash_Glyph *fash, int item)
94 {
95    int maj, min;
96
97    item &= 0xffff; // fixme: to do > 65k
98    maj = (item >> 8) & 0xff;
99    min = item & 0xff;
100    if (!fash->bucket[maj]) return NULL;
101    return fash->bucket[maj]->item[min];
102 }
103
104 static void
105 _fash_gl_add(Fash_Glyph *fash, int item, RGBA_Font_Glyph *glyph)
106 {
107    int maj, min;
108    
109    item &= 0xffff; // fixme: to do > 65k
110    maj = (item >> 8) & 0xff;
111    min = item & 0xff;
112    if (!fash->bucket[maj])
113      fash->bucket[maj] = calloc(1, sizeof(Fash_Int_Map));
114    fash->bucket[maj]->item[min] = glyph;
115 }
116
117
118
119
120
121 EAPI RGBA_Font_Glyph *
122 evas_common_font_int_cache_glyph_get(RGBA_Font_Int *fi, FT_UInt index)
123 {
124    RGBA_Font_Glyph *fg;
125    FT_UInt hindex;
126    FT_Error error;
127    const FT_Int32 hintflags[3] =
128      { FT_LOAD_NO_HINTING, FT_LOAD_FORCE_AUTOHINT, FT_LOAD_NO_AUTOHINT };
129
130    if (fi->fash)
131      {
132         fg = _fash_gl_find(fi->fash, index);
133         if (fg == (void *)(-1)) return NULL;
134         else if (fg) return fg;
135      }
136    
137    hindex = index + (fi->hinting * 500000000);
138    
139 //   fg = eina_hash_find(fi->glyphs, &hindex);
140 //   if (fg) return fg;
141
142    FTLOCK();
143 //   error = FT_Load_Glyph(fi->src->ft.face, index, FT_LOAD_NO_BITMAP);
144    error = FT_Load_Glyph(fi->src->ft.face, index,
145                          FT_LOAD_RENDER | hintflags[fi->hinting]);
146    FTUNLOCK();
147    if (error)
148      {
149         if (!fi->fash) fi->fash = _fash_gl_new();
150         if (fi->fash) _fash_gl_add(fi->fash, index, (void *)(-1));
151         return NULL;
152      }
153
154    fg = malloc(sizeof(struct _RGBA_Font_Glyph));
155    if (!fg) return NULL;
156    memset(fg, 0, (sizeof(struct _RGBA_Font_Glyph)));
157
158    FTLOCK();
159    error = FT_Get_Glyph(fi->src->ft.face->glyph, &(fg->glyph));
160    FTUNLOCK();
161    if (error)
162      {
163         free(fg);
164         if (!fi->fash) fi->fash = _fash_gl_new();
165         if (fi->fash) _fash_gl_add(fi->fash, index, (void *)(-1));
166         return NULL;
167      }
168    if (fg->glyph->format != FT_GLYPH_FORMAT_BITMAP)
169      {
170         FTLOCK();
171         error = FT_Glyph_To_Bitmap(&(fg->glyph), FT_RENDER_MODE_NORMAL, 0, 1);
172         if (error)
173           {
174              FT_Done_Glyph(fg->glyph);
175         FTUNLOCK();
176              free(fg);
177              if (!fi->fash) fi->fash = _fash_gl_new();
178              if (fi->fash) _fash_gl_add(fi->fash, index, (void *)(-1));
179              return NULL;
180           }
181    FTUNLOCK();
182      }
183    fg->glyph_out = (FT_BitmapGlyph)fg->glyph;
184    fg->index = hindex;
185
186    fg->fi = fi;
187   
188    if (!fi->fash) fi->fash = _fash_gl_new();
189    if (fi->fash) _fash_gl_add(fi->fash, index, fg);
190    
191 //   eina_hash_direct_add(fi->glyphs, &fg->index, fg);
192    return fg;
193 }
194
195 typedef struct _Font_Char_Index Font_Char_Index;
196 struct _Font_Char_Index
197 {
198    FT_UInt index;
199    int gl;
200 };
201
202 static FT_UInt
203 _evas_common_get_char_index(RGBA_Font_Int* fi, int gl)
204 {
205    Font_Char_Index result;
206    FT_UInt ret;
207
208 #ifdef HAVE_PTHREAD
209 ///   pthread_mutex_lock(&fi->ft_mutex);
210 #endif
211
212 //   result = eina_hash_find(fi->indexes, &gl);
213 //   if (result) goto on_correct;
214 //
215 //   result = malloc(sizeof (Font_Char_Index));
216 //   if (!result)
217 //     {
218 //#ifdef HAVE_PTHREAD
219 //      pthread_mutex_unlock(&fi->ft_mutex);
220 //#endif
221 //      return FT_Get_Char_Index(fi->src->ft.face, gl);
222 //     }
223
224    FTLOCK();
225    result.index = FT_Get_Char_Index(fi->src->ft.face, gl);
226    FTUNLOCK();
227    result.gl = gl;
228
229 //   eina_hash_direct_add(fi->indexes, &result->gl, result);
230 //
231 // on_correct:
232 #ifdef HAVE_PTHREAD
233 //   pthread_mutex_unlock(&fi->ft_mutex);
234 #endif
235    return result.index;
236 }
237
238 EAPI int
239 evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, int gl)
240 {
241    Eina_List *l;
242
243    if (fn->fash)
244      {
245         Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl);
246         if (fm)
247           {
248              if (fm->fint)
249                {
250                   *fi_ret = fm->fint;
251                   return fm->index;
252                }
253              else if (fm->index == -1) return 0;
254           }
255      }
256    
257    for (l = fn->fonts; l; l = l->next)
258      {
259         RGBA_Font_Int *fi;
260         int index;
261
262         fi = l->data;
263
264 #if 0 /* FIXME: charmap user is disabled and use a deprecated data type. */
265 /*        
266         if (fi->src->charmap) // Charmap loaded, FI/FS blank
267           {
268              index = evas_array_hash_search(fi->src->charmap, gl);
269              if (index != 0)
270                {
271                   evas_common_font_source_load_complete(fi->src);
272                   evas_common_font_int_load_complete(fi);
273
274                   evas_array_hash_free(fi->src->charmap);
275                   fi->src->charmap = NULL;
276
277                   *fi_ret = fi;
278                   return index;
279                }
280           }
281         else
282 */
283 #endif
284         if (!fi->src->ft.face) /* Charmap not loaded, FI/FS blank */
285           {
286              if (evas_common_font_source_load_complete(fi->src))
287                return 0;
288 #if 0 /* FIXME: disable this. this can eat a LOT of memory and in my tests with expedite at any rate shows no visible improvements */
289 /*             
290              index = FT_Get_Char_Index(fi->src->ft.face, gl);
291              if (index == 0)
292                {
293                   // Load Hash
294                   FT_ULong  charcode;
295                   FT_UInt   gindex;
296
297                   fi->src->charmap = evas_array_hash_new();
298                   charcode = FT_Get_First_Char(fi->src->ft.face, &gindex);
299                   while (gindex != 0)
300                     {
301                        evas_array_hash_add(fi->src->charmap, charcode, gindex);
302                        charcode = FT_Get_Next_Char(fi->src->ft.face, charcode, &gindex);
303                     }
304
305                   // Free face
306                   FT_Done_Face(fi->src->ft.face);
307                   fi->src->ft.face = NULL;
308                }
309              else
310                {
311                   evas_common_font_int_load_complete(fi);
312
313                   *fi_ret = fi;
314                   return index;
315                }
316  */
317 #endif
318           }
319         else /* Charmap not loaded, FS loaded */
320           {
321              index = _evas_common_get_char_index(fi, gl);
322              if (index != 0)
323                {
324                   if (!fi->ft.size)
325                     evas_common_font_int_load_complete(fi);
326                   if (!fn->fash) fn->fash = _fash_int_new();
327                   if (fn->fash) _fash_int_add(fn->fash, gl, fi, index);
328                   *fi_ret = fi;
329                   return index;
330                }
331              else
332                {
333                   if (!fn->fash) fn->fash = _fash_int_new();
334                   if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1);
335                }
336           }
337      }
338    return 0;
339 }
340
341
342
343 static void
344 evas_common_font_draw_internal(RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Font *fn, int x, int y, const char *in_text,
345                                RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, RGBA_Font_Int *fi,
346                                int im_w, int im_h __UNUSED__, int use_kerning
347                                )
348 {
349    int pen_x, pen_y;
350    int chr;
351    const char *text = in_text;
352    FT_Face pface = NULL;
353    FT_UInt prev_index;
354    DATA32 *im;
355    int c;
356    int char_index = 0; /* the index of the current char */
357
358 #ifdef INTERNATIONAL_SUPPORT
359    int len = 0;
360    /*FIXME: should get the direction by parmater */
361    EvasIntlParType direction = FRIBIDI_TYPE_ON;
362    EvasIntlLevel *level_list;
363
364    /* change the text to visual ordering and update the level list
365     * for as minimum impact on the code as possible just use text as an
366     * holder, will change in the future.*/
367    char *visual_text = evas_intl_utf8_to_visual(in_text, &len, &direction, NULL, NULL, &level_list);
368    text = (visual_text) ? visual_text : in_text;
369    
370 #endif
371
372    pen_x = x;
373    pen_y = y;
374    prev_index = 0;
375    im = dst->image.data;
376    for (char_index = 0, c = 0, chr = 0; text[chr]; char_index++)
377      {
378         FT_UInt index;
379         RGBA_Font_Glyph *fg;
380         int chr_x, chr_y;
381         int gl, kern;
382
383         gl = evas_common_font_utf8_get_next((unsigned char *)text, &chr);
384
385         if (gl == 0) break;
386         index = evas_common_font_glyph_search(fn, &fi, gl);
387         LKL(fi->ft_mutex);
388         if (fi->src->current_size != fi->size)
389           {
390              FT_Activate_Size(fi->ft.size);
391              fi->src->current_size = fi->size;
392           }
393         /* hmmm kerning means i can't sanely do my own cached metric tables! */
394         /* grrr - this means font face sharing is kinda... not an option if */
395         /* you want performance */
396           if ((use_kerning) && (prev_index) && (index) &&
397              (pface == fi->src->ft.face))
398             {
399 #ifdef INTERNATIONAL_SUPPORT
400                /* if it's rtl, the kerning matching should be reversed, i.e prev
401                 * index is now the index and the other way around. */
402                if (evas_intl_is_rtl_char(level_list, char_index))
403                  {
404                     if (evas_common_font_query_kerning(fi, index, prev_index, &kern))
405                       pen_x += kern;
406                  }
407                else
408 #endif
409                  {
410
411                     if (evas_common_font_query_kerning(fi, prev_index, index, &kern))
412                       pen_x += kern;
413                  }
414             }
415
416           pface = fi->src->ft.face;
417           fg = evas_common_font_int_cache_glyph_get(fi, index);
418           LKU(fi->ft_mutex);
419           if (!fg) continue;
420
421           if (dc->font_ext.func.gl_new)
422             {
423                /* extension calls */
424                fg->ext_dat = dc->font_ext.func.gl_new(dc->font_ext.data, fg);
425                fg->ext_dat_free = dc->font_ext.func.gl_free;
426             }
427
428           chr_x = (pen_x + (fg->glyph_out->left));
429           chr_y = (pen_y + (fg->glyph_out->top));
430
431           if (chr_x < (ext_x + ext_w))
432             {
433                DATA8 *data;
434                int i, j, w, h;
435
436                data = fg->glyph_out->bitmap.buffer;
437                j = fg->glyph_out->bitmap.pitch;
438                w = fg->glyph_out->bitmap.width;
439                if (j < w) j = w;
440                h = fg->glyph_out->bitmap.rows;
441                /*
442                   if ((fg->glyph_out->bitmap.pixel_mode == ft_pixel_mode_grays)
443                   && (fg->glyph_out->bitmap.num_grays == 256)
444                   )
445                   */
446                  {
447                     if ((j > 0) && (chr_x + w > ext_x))
448                       {
449                          if ((fg->ext_dat) && (dc->font_ext.func.gl_draw))
450                            {
451                               /* ext glyph draw */
452                               dc->font_ext.func.gl_draw(dc->font_ext.data,
453                                     (void *)dst,
454                                     dc, fg,
455                                     chr_x,
456                                     y - (chr_y - y)
457                                     );
458                            }
459                          else
460                            {
461                               if ((fg->glyph_out->bitmap.num_grays == 256) &&
462                                     (fg->glyph_out->bitmap.pixel_mode == ft_pixel_mode_grays))
463                                 {
464                                    for (i = 0; i < h; i++)
465                                      {
466                                         int dx, dy;
467                                         int in_x, in_w;
468
469                                         in_x = 0;
470                                         in_w = 0;
471                                         dx = chr_x;
472                                         dy = y - (chr_y - i - y);
473 #ifdef EVAS_SLI
474                                         if (((dy) % dc->sli.h) == dc->sli.y)
475 #endif
476                                           {
477                                              if ((dx < (ext_x + ext_w)) &&
478                                                    (dy >= (ext_y)) &&
479                                                    (dy < (ext_y + ext_h)))
480                                                {
481                                                   if (dx + w > (ext_x + ext_w))
482                                                     in_w += (dx + w) - (ext_x + ext_w);
483                                                   if (dx < ext_x)
484                                                     {
485                                                        in_w += ext_x - dx;
486                                                        in_x = ext_x - dx;
487                                                        dx = ext_x;
488                                                     }
489                                                   if (in_w < w)
490                                                     {
491                                                        func(NULL, data + (i * j) + in_x, dc->col.col,
492                                                              im + (dy * im_w) + dx, w - in_w);
493                                                     }
494                                                }
495                                           }
496                                      }
497                                 }
498                               else
499                                 {
500                                    DATA8 *tmpbuf = NULL, *dp, *tp, bits;
501                                    int bi, bj;
502                                    const DATA8 bitrepl[2] = {0x0, 0xff};
503
504                                    tmpbuf = alloca(w);
505                                    for (i = 0; i < h; i++)
506                                      {
507                                         int dx, dy;
508                                         int in_x, in_w, end;
509
510                                         in_x = 0;
511                                         in_w = 0;
512                                         dx = chr_x;
513                                         dy = y - (chr_y - i - y);
514 #ifdef EVAS_SLI
515                                         if (((dy) % dc->sli.h) == dc->sli.y)
516 #endif
517                                           {
518                                              tp = tmpbuf;
519                                              dp = data + (i * fg->glyph_out->bitmap.pitch);
520                                              for (bi = 0; bi < w; bi += 8)
521                                                {
522                                                   bits = *dp;
523                                                   if ((w - bi) < 8) end = w - bi;
524                                                   else end = 8;
525                                                   for (bj = 0; bj < end; bj++)
526                                                     {
527                                                        *tp = bitrepl[(bits >> (7 - bj)) & 0x1];
528                                                        tp++;
529                                                     }
530                                                   dp++;
531                                                }
532                                              if ((dx < (ext_x + ext_w)) &&
533                                                    (dy >= (ext_y)) &&
534                                                    (dy < (ext_y + ext_h)))
535                                                {
536                                                   if (dx + w > (ext_x + ext_w))
537                                                     in_w += (dx + w) - (ext_x + ext_w);
538                                                   if (dx < ext_x)
539                                                     {
540                                                        in_w += ext_x - dx;
541                                                        in_x = ext_x - dx;
542                                                        dx = ext_x;
543                                                     }
544                                                   if (in_w < w)
545                                                     {
546                                                        func(NULL, tmpbuf + in_x, dc->col.col,
547                                                              im + (dy * im_w) + dx, w - in_w);
548                                                     }
549                                                }
550                                           }
551                                      }
552                                 }
553                            }
554                          c++;
555                       }
556                  }
557             }
558           else
559             break;
560           pen_x += fg->glyph->advance.x >> 16;
561           prev_index = index;
562      }
563 #ifdef INTERNATIONAL_SUPPORT
564    if (level_list) free(level_list);
565    if (visual_text) free(visual_text);
566 #endif
567 }
568
569 EAPI void
570 evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Font *fn, int x, int y, const char *text)
571 {
572    int ext_x, ext_y, ext_w, ext_h;
573    int im_w, im_h;
574    int use_kerning;
575    RGBA_Gfx_Func func;
576    RGBA_Font_Int *fi;
577    Cutout_Rects *rects;
578    Cutout_Rect  *r;
579    int          c, cx, cy, cw, ch;
580    int          i;
581
582    fi = fn->fonts->data;
583
584    im_w = dst->cache_entry.w;
585    im_h = dst->cache_entry.h;
586
587    ext_x = 0; ext_y = 0; ext_w = im_w; ext_h = im_h;
588    if (dc->clip.use)
589      {
590         ext_x = dc->clip.x;
591         ext_y = dc->clip.y;
592         ext_w = dc->clip.w;
593         ext_h = dc->clip.h;
594         if (ext_x < 0)
595           {
596              ext_w += ext_x;
597              ext_x = 0;
598           }
599         if (ext_y < 0)
600           {
601              ext_h += ext_y;
602              ext_y = 0;
603           }
604         if ((ext_x + ext_w) > im_w)
605           ext_w = im_w - ext_x;
606         if ((ext_y + ext_h) > im_h)
607           ext_h = im_h - ext_y;
608      }
609    if (ext_w <= 0) return;
610    if (ext_h <= 0) return;
611
612 #ifndef EVAS_FRAME_QUEUING
613    LKL(fn->lock);
614 #endif
615 //   evas_common_font_size_use(fn);
616    use_kerning = FT_HAS_KERNING(fi->src->ft.face);
617    func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst, 1, dc->render_op);
618
619    if (!dc->cutout.rects)
620      {
621         evas_common_font_draw_internal(dst, dc, fn, x, y, text,
622                                        func, ext_x, ext_y, ext_w, ext_h, fi,
623                                        im_w, im_h, use_kerning
624                                        );
625      }
626    else
627      {
628         c = dc->clip.use; cx = dc->clip.x; cy = dc->clip.y; cw = dc->clip.w; ch = dc->clip.h;
629         evas_common_draw_context_clip_clip(dc, 0, 0, dst->cache_entry.w, dst->cache_entry.h);
630         /* our clip is 0 size.. abort */
631         if ((dc->clip.w > 0) && (dc->clip.h > 0))
632           {
633              rects = evas_common_draw_context_apply_cutouts(dc);
634              for (i = 0; i < rects->active; ++i)
635                {
636                   r = rects->rects + i;
637                   evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
638                   evas_common_font_draw_internal(dst, dc, fn, x, y, text,
639                                                  func, r->x, r->y, r->w, r->h, fi,
640                                                  im_w, im_h, use_kerning
641                                                  );
642                }
643              evas_common_draw_context_apply_clear_cutouts(rects);
644           }
645         dc->clip.use = c; dc->clip.x = cx; dc->clip.y = cy; dc->clip.w = cw; dc->clip.h = ch;
646      }
647 #ifndef EVAS_FRAME_QUEUING
648    LKU(fn->lock);
649 #endif
650 }