evas: Use refcounted glyph arrays
authorLeandro Pereira <leandro@profusion.mobi>
Mon, 17 Dec 2012 21:27:07 +0000 (21:27 +0000)
committerLeandro Pereira <leandro@profusion.mobi>
Mon, 17 Dec 2012 21:27:07 +0000 (21:27 +0000)
This is in preparation for threaded render landing: the render thread will
hold a reference to a text object's glyphs while it hasn't been rendered
yet (and will drop that reference after drawing). This changes the internal
API a little bit (evas_common_font_rgba_draw() now takes an Evas_Glyph_Array
instead of an Evas_Text_Props).

SVN revision: 81183

src/lib/evas/common/evas_font.h
src/lib/evas/common/evas_font_draw.c
src/lib/evas/common/evas_text_utils.c
src/lib/evas/common/evas_text_utils.h
src/modules/evas/engines/gl_x11/evas_engine.c
src/modules/evas/engines/software_generic/evas_engine.c

index b259e63..5ea15bc 100644 (file)
@@ -17,11 +17,11 @@ EAPI int               evas_common_font_max_descent_get      (RGBA_Font *fn);
 EAPI int               evas_common_font_get_line_advance     (RGBA_Font *fn);
 
 /* draw */
+typedef void (*Evas_Common_Font_Draw_Cb)(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, int im_w, int im_h);
 
-typedef void (*Evas_Common_Font_Draw_Cb)(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, const Evas_Text_Props *text_props, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, int im_w, int im_h);
-
-EAPI void              evas_common_font_draw_cb              (RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, const Evas_Text_Props *intl_props, Evas_Common_Font_Draw_Cb cb);
-EAPI void              evas_common_font_draw                 (RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, const Evas_Text_Props *intl_props);
+EAPI void              evas_common_font_draw_cb              (RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, Evas_Common_Font_Draw_Cb cb);
+EAPI void              evas_common_font_draw                 (RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs);
+EAPI void              evas_common_font_rgba_draw            (RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, int im_w, int im_h);
 EAPI int               evas_common_font_glyph_search         (RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl);
 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);
index 7848842..8bc57a3 100644 (file)
@@ -29,32 +29,29 @@ evas_common_font_draw_init(void)
  */
 static void
 evas_common_font_draw_internal(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y,
-                               const Evas_Text_Props *text_props, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w,
+                               Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w,
                                int ext_h, int im_w, int im_h EINA_UNUSED)
 {
    DATA32 *im;
-   Evas_Glyph *glyphs;
-   unsigned int length;
-   unsigned int it;
+   Evas_Glyph *glyph;
 
-   im = dst->image.data;
+   if (!glyphs) return;
+   if (!glyphs->array) return;
 
-   if (!text_props->glyphs) return ;
+   im = dst->image.data;
 
-   glyphs = text_props->glyphs;
-   length = text_props->glyphs_length;
-   for (it = 0; it < length; ++it, ++glyphs)
+   EINA_INARRAY_FOREACH(glyphs->array, glyph)
      {
         RGBA_Font_Glyph *fg;
         int chr_x, chr_y;
 
-        fg = glyphs->fg;
+        fg = glyph->fg;
 
        /* FIXME: Why was that moved out of prepare ? This increase cache miss. */
-        glyphs->coord.w = fg->glyph_out->bitmap.width;
-        glyphs->coord.h = fg->glyph_out->bitmap.rows;
-        glyphs->j = fg->glyph_out->bitmap.pitch;
-        glyphs->data = fg->glyph_out->bitmap.buffer;
+        glyph->coord.w = fg->glyph_out->bitmap.width;
+        glyph->coord.h = fg->glyph_out->bitmap.rows;
+        glyph->j = fg->glyph_out->bitmap.pitch;
+        glyph->data = fg->glyph_out->bitmap.buffer;
 
         if (dc->font_ext.func.gl_new)
           {
@@ -63,19 +60,19 @@ evas_common_font_draw_internal(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, in
              fg->ext_dat_free = dc->font_ext.func.gl_free;
           }
 
-        chr_x = x + glyphs->coord.x/* EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR */;
-        chr_y = y + glyphs->coord.y/* EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR */;
+        chr_x = x + glyph->coord.x;
+        chr_y = y + glyph->coord.y;
 
         if (chr_x < (ext_x + ext_w))
           {
              DATA8 *data;
              int i, j, w, h;
 
-             data = glyphs->data;
-             j = glyphs->j;
-             w = glyphs->coord.w;
+             data = glyph->data;
+             j = glyph->j;
+             w = glyph->coord.w;
              if (j < w) j = w;
-             h = glyphs->coord.h;
+             h = glyph->coord.h;
 
 #ifdef HAVE_PIXMAN
 # ifdef PIXMAN_FONT             
@@ -226,26 +223,196 @@ evas_common_font_draw_internal(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, in
 }
 
 EAPI void
+evas_common_font_rgba_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, int im_w, int im_h EINA_UNUSED)
+{
+   DATA32 *im;
+   Evas_Glyph *glyph;
+
+   if (!glyphs) return;
+   if (!glyphs->array) return;
+
+   im = dst->image.data;
+
+   EINA_INARRAY_FOREACH(glyphs->array, glyph)
+     {
+        RGBA_Font_Glyph *fg;
+        int chr_x, chr_y;
+
+        fg = glyph->fg;
+
+       /* FIXME: Why was that moved out of prepare ? This increase cache miss. */
+        glyph->coord.w = fg->glyph_out->bitmap.width;
+        glyph->coord.h = fg->glyph_out->bitmap.rows;
+        glyph->j = fg->glyph_out->bitmap.pitch;
+        glyph->data = fg->glyph_out->bitmap.buffer;
+
+        if (dc->font_ext.func.gl_new)
+          {
+             /* extension calls */
+             fg->ext_dat = dc->font_ext.func.gl_new(dc->font_ext.data, fg);
+             fg->ext_dat_free = dc->font_ext.func.gl_free;
+          }
+
+        chr_x = x + glyph->coord.x;
+        chr_y = y + glyph->coord.y;
+
+        if (chr_x < (ext_x + ext_w))
+          {
+             DATA8 *data;
+             int i, j, w, h;
+
+             data = glyph->data;
+             j = glyph->j;
+             w = glyph->coord.w;
+             if (j < w) j = w;
+             h = glyph->coord.h;
+
+             if ((j > 0) && (chr_x + w > ext_x))
+               {
+                  if ((fg->ext_dat) && (dc->font_ext.func.gl_draw))
+                    {
+                       /* ext glyph draw */
+                       dc->font_ext.func.gl_draw(dc->font_ext.data,
+                                                 (void *)dst,
+                                                 dc, fg, chr_x,
+                                                 y - (chr_y - y));
+                    }
+                  else
+                    {
+                       if ((fg->glyph_out->bitmap.num_grays == 256) &&
+                           (fg->glyph_out->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY))
+                         {
+                            for (i = 0; i < h; i++)
+                              {
+                                 int dx, dy;
+                                 int in_x, in_w;
+
+                                 in_x = 0;
+                                 in_w = 0;
+                                 dx = chr_x;
+                                 dy = y - (chr_y - i - y);
+
+                                 if ((dx < (ext_x + ext_w)) &&
+                                     (dy >= (ext_y)) &&
+                                     (dy < (ext_y + ext_h)))
+                                   {
+                                      if (dx + w > (ext_x + ext_w))
+                                        in_w += (dx + w) - (ext_x + ext_w);
+                                      if (dx < ext_x)
+                                        {
+                                           in_w += ext_x - dx;
+                                           in_x = ext_x - dx;
+                                           dx = ext_x;
+                                        }
+                                      if (in_w < w)
+                                        {
+                                           func(NULL, data + (i * j) + in_x, dc->col.col,
+                                                im + (dy * im_w) + dx, w - in_w);
+                                        }
+                                   }
+                              }
+                         }
+                       else
+                         {
+                            DATA8 *tmpbuf = NULL, *dp, *tp, bits;
+                            int bi, bj;
+                            const DATA8 bitrepl[2] = {0x0, 0xff};
+
+                            tmpbuf = alloca(w);
+                            for (i = 0; i < h; i++)
+                              {
+                                 int dx, dy;
+                                 int in_x, in_w, end;
+
+                                 in_x = 0;
+                                 in_w = 0;
+                                 dx = chr_x;
+                                 dy = y - (chr_y - i - y);
+
+                                 tp = tmpbuf;
+                                 dp = data + (i * fg->glyph_out->bitmap.pitch);
+                                 for (bi = 0; bi < w; bi += 8)
+                                   {
+                                      bits = *dp;
+                                      if ((w - bi) < 8) end = w - bi;
+                                      else end = 8;
+                                      for (bj = 0; bj < end; bj++)
+                                        {
+                                           *tp = bitrepl[(bits >> (7 - bj)) & 0x1];
+                                           tp++;
+                                        }
+                                      dp++;
+                                   }
+                                 if ((dx < (ext_x + ext_w)) &&
+                                     (dy >= (ext_y)) &&
+                                     (dy < (ext_y + ext_h)))
+                                   {
+                                      if (dx + w > (ext_x + ext_w))
+                                        in_w += (dx + w) - (ext_x + ext_w);
+                                      if (dx < ext_x)
+                                        {
+                                           in_w += ext_x - dx;
+                                           in_x = ext_x - dx;
+                                           dx = ext_x;
+                                        }
+                                      if (in_w < w)
+                                        {
+                                           func(NULL, tmpbuf + in_x, dc->col.col,
+                                                im + (dy * im_w) + dx, w - in_w);
+                                        }
+                                   }
+                              }
+                         }
+                    }
+               }
+          } else break;
+     }
+}
+
+void
+evas_common_font_glyphs_ref(Evas_Glyph_Array *array)
+{
+   if (!array) return;
+
+   eina_lock_take(&array->lock);
+   array->refcount++;
+   eina_lock_release(&array->lock);
+}
+
+void
+evas_common_font_glyphs_unref(Evas_Glyph_Array *array)
+{
+   if (!array) return;
+
+   eina_lock_take(&array->lock);
+   if (--array->refcount)
+     {
+        eina_lock_release(&array->lock);
+        return;
+     }
+
+   eina_lock_release(&array->lock);
+   eina_lock_free(&array->lock);
+
+   eina_inarray_free(array->array);
+   free(array);
+}
+
+EAPI void
 evas_common_font_draw_prepare(Evas_Text_Props *text_props)
 {
    RGBA_Font_Int *fi;
    RGBA_Font_Glyph *fg;
-   Evas_Glyph *glyphs;
-   int glyphs_length;
-   int glyphs_max;
+   Eina_Inarray *glyphs;
    EVAS_FONT_WALK_TEXT_INIT();
 
    fi = text_props->font_instance;
    if (!fi) return;
 
    if (!text_props->changed && text_props->generation == fi->generation && text_props->glyphs)
-     return ;
-
-   glyphs = text_props->glyphs;
-   glyphs_length = 0;
-   glyphs_max = text_props->glyphs_length;
-   text_props->glyphs_length = 0;
+     return;
 
+   glyphs = eina_inarray_new(sizeof(Evas_Glyph), 32);
    evas_common_font_int_reload(fi);
 
    if (fi->src->current_size != fi->size)
@@ -259,7 +426,7 @@ evas_common_font_draw_prepare(Evas_Text_Props *text_props)
 
    EVAS_FONT_WALK_TEXT_START()
      {
-        Evas_Glyph *glyph;
+        Evas_Glyph glyph;
         FT_UInt idx;
 
         if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
@@ -269,37 +436,43 @@ evas_common_font_draw_prepare(Evas_Text_Props *text_props)
         if (!fg) continue;
         if (!fg->glyph_out) evas_common_font_int_cache_glyph_render(fg);
 
-       if (glyphs_length + 1 >= glyphs_max)
-         {
-             Evas_Glyph *tmp;
-
-             glyphs_max += 8;
-             tmp = realloc(glyphs, glyphs_max * sizeof (Evas_Glyph));
-             if (!tmp) return ;
-             glyphs = tmp;
-             text_props->glyphs = glyphs;
-         }
-
-        glyph = glyphs + glyphs_length++;
+        glyph.fg = fg;
+        glyph.idx = idx;
+        glyph.coord.x = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR;
+        glyph.coord.y = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR;
 
-        glyph->fg = fg;
-        glyph->idx = idx;
-        glyph->coord.x = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR;
-        glyph->coord.y = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR;
+        if (eina_inarray_push(glyphs, &glyph) < 0) goto error;
      }
    EVAS_FONT_WALK_TEXT_END();
 
-   text_props->glyphs_length = glyphs_length;
-   text_props->glyphs = glyphs;
+   /*
+    * Clearing the reference to the glyph array is fine, since this
+    * reference is only used to use this from another thread, which is now
+    * holding the reference.
+    */
+   evas_common_font_glyphs_unref(text_props->glyphs);
+
+   text_props->glyphs = malloc(sizeof(*text_props->glyphs));
+   if (!text_props->glyphs) goto error;
+   text_props->glyphs->refcount = 1;
+   text_props->glyphs->array = glyphs;
+   if (!eina_lock_new(&text_props->glyphs->lock))
+     CRIT("Could not create glyph array lock");
+
    /* check if there's a request queue in fi, if so ask cserve2 to render
     * those glyphs
     */
 
    text_props->generation = fi->generation;
+
+   return;
+
+error:
+   eina_inarray_free(glyphs);
 }
 
 EAPI void
-evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, const Evas_Text_Props *text_props, Evas_Common_Font_Draw_Cb cb)
+evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, Evas_Common_Font_Draw_Cb cb)
 {
    static Cutout_Rects *rects = NULL;
    int ext_x, ext_y, ext_w, ext_h;
@@ -342,7 +515,7 @@ evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, c
 
    if (!dc->cutout.rects)
      {
-        cb(dst, dc, x, y, text_props,
+        cb(dst, dc, x, y, glyphs,
            func, ext_x, ext_y, ext_w, ext_h,
            im_w, im_h);
      }
@@ -358,7 +531,7 @@ evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, c
                {
                   r = rects->rects + i;
                   evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
-                  cb(dst, dc, x, y, text_props,
+                  cb(dst, dc, x, y, glyphs,
                      func, r->x, r->y, r->w, r->h,
                      im_w, im_h);
                }
@@ -368,9 +541,9 @@ evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, c
 }
 
 EAPI void
-evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, const Evas_Text_Props *text_props)
+evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs)
 {
-   evas_common_font_draw_cb(dst, dc, x, y, text_props,
+   evas_common_font_draw_cb(dst, dc, x, y, glyphs,
                             evas_common_font_draw_internal);
 }
 
@@ -392,7 +565,7 @@ evas_common_font_draw_do(const Cutout_Rects *reuse, const Eina_Rectangle *clip,
         evas_common_draw_context_clip_clip(dc,
                                            clip->x, clip->y,
                                            clip->w, clip->h);
-        evas_common_font_draw_internal(dst, dc, x, y, text_props,
+        evas_common_font_draw_internal(dst, dc, x, y, text_props->glyphs,
                                        func,
                                        dc->clip.x, dc->clip.y,
                                        dc->clip.w, dc->clip.h,
@@ -407,7 +580,7 @@ evas_common_font_draw_do(const Cutout_Rects *reuse, const Eina_Rectangle *clip,
         EINA_RECTANGLE_SET(&area, r->x, r->y, r->w - 1, r->h - 1);
         if (!eina_rectangle_intersection(&area, clip)) continue ;
         evas_common_draw_context_set_clip(dc, area.x, area.y, area.w, area.h);
-        evas_common_font_draw_internal(dst, dc, x, y, text_props,
+        evas_common_font_draw_internal(dst, dc, x, y, text_props->glyphs,
                                        func, area.x, area.y, area.w, area.h,
                                        im_w, im_h);
      }
index eb4fc27..71b4fa6 100644 (file)
@@ -37,7 +37,6 @@ evas_common_text_props_content_copy_and_ref(Evas_Text_Props *dst,
 {
    memcpy(dst, src, sizeof(Evas_Text_Props));
    dst->glyphs = NULL;
-   dst->glyphs_length = 0;
    evas_common_text_props_content_ref(dst);
 }
 
@@ -68,9 +67,10 @@ evas_common_text_props_content_nofree_unref(Evas_Text_Props *props)
              props->font_instance = NULL;
           }
 
-        free(props->glyphs);
+        evas_common_font_glyphs_unref(props->glyphs);
+        /* After unreferencing the glyph array, a thread will still hold
+         * a reference, so this can be safely set to NULL. */
         props->glyphs = NULL;
-        props->glyphs_length = 0;
         
         if (props->info->glyph)
           free(props->info->glyph);
@@ -90,9 +90,10 @@ evas_common_text_props_content_unref(Evas_Text_Props *props)
    if (!props->info)
       return;
    
-   free(props->glyphs);
+   evas_common_font_glyphs_unref(props->glyphs);
+   /* After unreferencing the glyph array, a thread will still hold
+    * a reference, so this can be safely set to NULL. */
    props->glyphs = NULL;
-   props->glyphs_length = 0;
 
    if (--(props->info->refcount) == 0)
      {
index 3f61569..7ce2526 100644 (file)
@@ -19,6 +19,14 @@ typedef enum
 #define REPLACEMENT_CHAR 0xFFFD
 
 typedef struct _Evas_Glyph Evas_Glyph;
+typedef struct _Evas_Glyph_Array Evas_Glyph_Array;
+
+struct _Evas_Glyph_Array
+{
+   Eina_Inarray *array;
+   unsigned int refcount;
+   Eina_Lock lock;
+};
 
 struct _Evas_Text_Props
 {
@@ -33,8 +41,7 @@ struct _Evas_Text_Props
    Evas_Text_Props_Info *info;
    void *font_instance;
 
-   Evas_Glyph *glyphs;
-   int glyphs_length;
+   Evas_Glyph_Array *glyphs;
 
    int generation;
    Eina_Bool changed : 1;
@@ -62,6 +69,11 @@ struct _Evas_Font_Glyph_Info
 };
 
 void
+evas_common_font_glyphs_ref(Evas_Glyph_Array *array);
+void
+evas_common_font_glyphs_unref(Evas_Glyph_Array *array);
+
+void
 evas_common_text_props_bidi_set(Evas_Text_Props *props,
       Evas_BiDi_Paragraph_Props *bidi_par_props, size_t start);
 
index 9814d2b..35bbd95 100644 (file)
@@ -2625,7 +2625,7 @@ eng_font_draw(void *data, void *context, void *surface, Evas_Font_Set *font EINA
                                               evas_gl_font_texture_free,
                                               evas_gl_font_texture_draw);
         evas_common_font_draw_prepare(intl_props);
-        evas_common_font_draw(im, context, x, y, intl_props);
+        evas_common_font_draw(im, context, x, y, intl_props->glyphs);
         evas_common_draw_context_font_ext_set(context,
                                               NULL,
                                               NULL,
index 9efd35e..e16795d 100644 (file)
@@ -1260,8 +1260,8 @@ eng_font_draw(void *data EINA_UNUSED, void *context, void *surface, Evas_Font_Se
 #endif   
      {
         evas_common_font_draw_prepare(text_props);
-       evas_common_font_draw(surface, context, x, y, text_props);
-       evas_common_cpu_end_opt();
+        evas_common_font_draw(surface, context, x, y, text_props->glyphs);
+        evas_common_cpu_end_opt();
      }
 }