forked from enlightenment/efl
Evas textblock: split the visual layouting to two stages:
1. Split to logical layout units. 2. Order the logical layout units in a visual way. This lets us cache the results of the first part (unless the text changes), which makes everything a lot faster in the OT case, and quite faster in the regular case. There are still some issues unresolved in this commit that will be resolved in future commits: 1. No support for async rendering - yes, I forgot I'm supposed to support that. 2. native_size should probably be calculated in another way because the current one is slow and if we already have the logical items it should be a piece of cake... SVN revision: 56504
This commit is contained in:
parent
a2fc127e8c
commit
b46c83aeac
|
@ -1857,10 +1857,10 @@ _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n)
|
|||
|
||||
/**
|
||||
* @internal
|
||||
* Free the layout paragraph and all of it's lines.
|
||||
* Free the visual lines in the paragraph (logical items are kept)
|
||||
*/
|
||||
static void
|
||||
_paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
|
||||
_paragraph_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
|
||||
{
|
||||
while (par->lines)
|
||||
{
|
||||
|
@ -1870,6 +1870,16 @@ _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
|
|||
par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
|
||||
_line_free(obj, ln);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Free the layout paragraph and all of it's lines and logical items.
|
||||
*/
|
||||
static void
|
||||
_paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
|
||||
{
|
||||
_paragraph_clear(obj, par);
|
||||
|
||||
{
|
||||
Eina_List *i, *i_prev;
|
||||
|
@ -1892,6 +1902,28 @@ _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
|
|||
*/
|
||||
static void
|
||||
_paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
|
||||
{
|
||||
Evas_Object_Textblock *o;
|
||||
Evas_Object_Textblock_Paragraph *par;
|
||||
o = (Evas_Object_Textblock *)(obj->object_data);
|
||||
|
||||
EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
|
||||
{
|
||||
_paragraph_clear(obj, par);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Free the paragraphs from the inlist pars, the difference between this and
|
||||
* _paragraphs_clear is that the latter keeps the logical items and the par
|
||||
* items, while the former frees them as well.
|
||||
*
|
||||
* @param obj the evas object - Not NULL.
|
||||
* @param pars the paragraphs to clean - Not NULL.
|
||||
*/
|
||||
static void
|
||||
_paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
|
||||
{
|
||||
Evas_Object_Textblock *o;
|
||||
o = (Evas_Object_Textblock *)(obj->object_data);
|
||||
|
@ -2373,7 +2405,8 @@ _layout_item_text_split_strip_white(Ctxt *c,
|
|||
_layout_text_add_logical_item(c, ti->parent.format, new_ti, _ITEM(ti));
|
||||
|
||||
/* FIXME: Will break with kerning and a bunch of other stuff, should
|
||||
* maybe adjust the last adv of the prev and the offset of the cur*/
|
||||
* maybe adjust the last adv of the prev and the offset of the cur
|
||||
* There's also another similar fixme below (same case, not marked) */
|
||||
ti->parent.w -= new_ti->parent.w;
|
||||
ti->parent.adv -= new_ti->parent.adv;
|
||||
|
||||
|
@ -2399,6 +2432,38 @@ _layout_item_text_split_strip_white(Ctxt *c,
|
|||
return new_ti;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Merge item2 into item1 and free item2.
|
||||
*
|
||||
* @param c the context to work on - Not NULL.
|
||||
* @param item1 the item to copy to
|
||||
* @param item2 the item to copy from
|
||||
*/
|
||||
static void
|
||||
_layout_item_merge_and_free(Ctxt *c,
|
||||
Evas_Object_Textblock_Text_Item *item1,
|
||||
Evas_Object_Textblock_Text_Item *item2)
|
||||
{
|
||||
Eina_Unicode *tmp;
|
||||
size_t len1, len2;
|
||||
item1->parent.w += item2->parent.w;
|
||||
item1->parent.adv += item2->parent.adv;
|
||||
evas_common_text_props_merge(&item1->parent.text_props,
|
||||
&item2->parent.text_props);
|
||||
len1 = eina_unicode_strlen(item1->text);
|
||||
len2 = eina_unicode_strlen(item2->text);
|
||||
tmp = realloc(item1->text, (len1 + len2 + 1) * sizeof(Eina_Unicode));
|
||||
eina_unicode_strcpy(tmp + len1, item2->text);
|
||||
item1->text = tmp;
|
||||
item1->text[len1 + len2] = 0;
|
||||
|
||||
item1->parent.merge = EINA_FALSE;
|
||||
item1->parent.visually_deleted = EINA_FALSE;
|
||||
|
||||
_item_free(c->obj, NULL, _ITEM(item2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Return the start of the last word up until start.
|
||||
|
@ -3183,15 +3248,9 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
|
|||
}
|
||||
|
||||
|
||||
/* If there are no nodes and lines, do the initial creation. */
|
||||
if (!c->o->text_nodes)
|
||||
if (o->content_changed)
|
||||
{
|
||||
_layout_paragraph_new(c, NULL);
|
||||
_layout_line_new(c, fmt);
|
||||
_layout_text_append(c, fmt, NULL, 0, 0, NULL);
|
||||
_layout_line_finalize(c, fmt);
|
||||
}
|
||||
|
||||
_paragraphs_free(obj, o->paragraphs);
|
||||
/* Go through all the text nodes to create the logical layout */
|
||||
EINA_INLIST_FOREACH(c->o->text_nodes, n)
|
||||
{
|
||||
|
@ -3199,6 +3258,9 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
|
|||
size_t start;
|
||||
int off;
|
||||
|
||||
n->dirty = 0; /* Mark as if we cleaned the paragraph, although
|
||||
we should really use it to fine tune the
|
||||
changes here, and not just blindly mark */
|
||||
_layout_paragraph_new(c, n); /* Each node is a paragraph */
|
||||
|
||||
/* For each text node to thorugh all of it's format nodes
|
||||
|
@ -3214,7 +3276,8 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
|
|||
off += fnode->offset;
|
||||
/* No need to skip on the first run, or a non-visible one */
|
||||
_layout_text_append(c, fmt, n, start, off, o->repch);
|
||||
_layout_do_format(obj, c, &fmt, fnode, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
|
||||
_layout_do_format(obj, c, &fmt, fnode, &style_pad_l,
|
||||
&style_pad_r, &style_pad_t, &style_pad_b);
|
||||
if ((c->have_underline2) || (c->have_underline))
|
||||
{
|
||||
if (style_pad_b < c->underline_extend)
|
||||
|
@ -3237,6 +3300,47 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
|
|||
}
|
||||
_layout_text_append(c, fmt, n, start, -1, o->repch);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_paragraphs_clear(obj, o->paragraphs);
|
||||
c->paragraphs = o->paragraphs;
|
||||
/* Merge the ones that need merging. */
|
||||
/* Go through all the paragraphs, lines, items and merge if should be
|
||||
* merged we merge backwards!!! */
|
||||
Evas_Object_Textblock_Paragraph *par;
|
||||
EINA_INLIST_FOREACH(EINA_INLIST_GET(c->paragraphs), par)
|
||||
{
|
||||
Eina_List *itr, *itr_next;
|
||||
Evas_Object_Textblock_Item *it, *prev_it = NULL;
|
||||
EINA_LIST_FOREACH_SAFE(par->logical_items, itr, itr_next, it)
|
||||
{
|
||||
if (it->merge && prev_it &&
|
||||
(prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
|
||||
(it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
|
||||
{
|
||||
_layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
|
||||
_ITEM_TEXT(it));
|
||||
par->logical_items =
|
||||
eina_list_remove_list(par->logical_items, itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_it = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are no paragraphs, create the minimum needed */
|
||||
if (!c->paragraphs)
|
||||
{
|
||||
_layout_paragraph_new(c, NULL);
|
||||
_layout_line_new(c, fmt);
|
||||
_layout_text_append(c, fmt, NULL, 0, 0, NULL);
|
||||
_layout_line_finalize(c, fmt);
|
||||
}
|
||||
|
||||
|
||||
/* End of logical layout creation */
|
||||
|
||||
|
@ -3271,24 +3375,21 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
|
|||
|
||||
if (w_ret) *w_ret = c->wmax;
|
||||
if (h_ret) *h_ret = c->hmax;
|
||||
o->paragraphs = c->paragraphs;
|
||||
|
||||
if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
|
||||
(o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
|
||||
{
|
||||
c->par->lines = NULL;
|
||||
o->style_pad.l = style_pad_l;
|
||||
o->style_pad.r = style_pad_r;
|
||||
o->style_pad.t = style_pad_t;
|
||||
o->style_pad.b = style_pad_b;
|
||||
_layout(obj, calc_only, w, h, w_ret, h_ret);
|
||||
_paragraphs_clear(obj, c->paragraphs);
|
||||
_layout(obj, calc_only, w, h, w_ret, h_ret);
|
||||
return;
|
||||
}
|
||||
if (!calc_only)
|
||||
{
|
||||
o->paragraphs = c->paragraphs;
|
||||
|
||||
return;
|
||||
}
|
||||
if (c->paragraphs) _paragraphs_clear(obj, c->paragraphs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3301,17 +3402,13 @@ static void
|
|||
_relayout(const Evas_Object *obj)
|
||||
{
|
||||
Evas_Object_Textblock *o;
|
||||
Evas_Object_Textblock_Paragraph *paragraphs;
|
||||
|
||||
o = (Evas_Object_Textblock *)(obj->object_data);
|
||||
paragraphs = o->paragraphs;
|
||||
o->paragraphs = NULL;
|
||||
_layout(obj,
|
||||
0,
|
||||
obj->cur.geometry.w, obj->cur.geometry.h,
|
||||
&o->formatted.w, &o->formatted.h);
|
||||
o->formatted.valid = 1;
|
||||
if (paragraphs) _paragraphs_clear(obj, paragraphs);
|
||||
o->last_w = obj->cur.geometry.w;
|
||||
o->changed = 0;
|
||||
o->content_changed = 0;
|
||||
|
@ -7670,7 +7767,7 @@ evas_object_textblock_clear(Evas_Object *obj)
|
|||
}
|
||||
if (o->paragraphs)
|
||||
{
|
||||
_paragraphs_clear(obj, o->paragraphs);
|
||||
_paragraphs_free(obj, o->paragraphs);
|
||||
o->paragraphs = NULL;
|
||||
}
|
||||
_evas_textblock_text_node_changed(o, obj, NULL);
|
||||
|
@ -8146,20 +8243,12 @@ evas_object_textblock_render_pre(Evas_Object *obj)
|
|||
if ((o->changed) || (o->content_changed) ||
|
||||
(o->last_w != obj->cur.geometry.w))
|
||||
{
|
||||
Evas_Object_Textblock_Paragraph *paragraphs;
|
||||
|
||||
paragraphs = o->paragraphs;
|
||||
o->paragraphs = NULL;
|
||||
o->formatted.valid = 0;
|
||||
_layout(obj,
|
||||
0,
|
||||
obj->cur.geometry.w, obj->cur.geometry.h,
|
||||
&o->formatted.w, &o->formatted.h);
|
||||
o->formatted.valid = 1;
|
||||
if (paragraphs)
|
||||
{
|
||||
_paragraphs_clear(obj, paragraphs);
|
||||
}
|
||||
o->last_w = obj->cur.geometry.w;
|
||||
o->redraw = 0;
|
||||
evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
|
||||
|
@ -8378,15 +8467,6 @@ ptnode(Evas_Object_Textblock_Node_Text *n)
|
|||
printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
|
||||
printf("format_node = %p\n", n->format_node);
|
||||
printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
|
||||
if (n->bidi_props)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0 ; i < eina_ustrbuf_length_get(n->unicode) ; i++)
|
||||
{
|
||||
printf("%d", n->bidi_props->embedding_levels[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -135,7 +135,7 @@ evas_common_font_ot_cutoff_text_props(Evas_Text_Props *props, int cutoff)
|
|||
}
|
||||
|
||||
/* Won't work in the middle of ligatures
|
||||
* assumes ext doesn't have an already init ot_data
|
||||
* aissumes ext doesn't have an already init ot_data
|
||||
* we assume there's at least one char in each part */
|
||||
EAPI void
|
||||
evas_common_font_ot_split_text_props(Evas_Text_Props *base,
|
||||
|
@ -181,6 +181,7 @@ evas_common_font_ot_split_text_props(Evas_Text_Props *base,
|
|||
{
|
||||
ext->ot_data->items[i].source_cluster -= min;
|
||||
}
|
||||
ext->ot_data->offset = base->ot_data->offset + min;
|
||||
}
|
||||
tmp = realloc(base->ot_data->items,
|
||||
cutoff * sizeof(Evas_Font_OT_Data_Item));
|
||||
|
@ -189,6 +190,53 @@ evas_common_font_ot_split_text_props(Evas_Text_Props *base,
|
|||
|
||||
}
|
||||
|
||||
/* Won't work in the middle of ligatures
|
||||
* assumes both are init correctly and that both are from the
|
||||
* same origin item, i.e both have the same script + direction.
|
||||
* assume item1 is logically first */
|
||||
EAPI void
|
||||
evas_common_font_ot_merge_text_props(Evas_Text_Props *item1,
|
||||
const Evas_Text_Props *item2)
|
||||
{
|
||||
Evas_Font_OT_Data_Item *tmp, *itr; /* Itr will be used for adding back
|
||||
the offsets */
|
||||
size_t len;
|
||||
if (!item1->ot_data || !item2->ot_data)
|
||||
return;
|
||||
len = item1->ot_data->len + item2->ot_data->len;
|
||||
tmp = calloc(len, sizeof(Evas_Font_OT_Data_Item));
|
||||
if (item1->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
|
||||
{
|
||||
memcpy(tmp, item2->ot_data->items,
|
||||
item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
|
||||
memcpy(tmp + item2->ot_data->len, item1->ot_data->items,
|
||||
item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
|
||||
itr = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(tmp, item1->ot_data->items,
|
||||
item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
|
||||
memcpy(tmp + item1->ot_data->len, item2->ot_data->items,
|
||||
item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
|
||||
itr = tmp + item1->ot_data->len;
|
||||
}
|
||||
free(item1->ot_data->items);
|
||||
item1->ot_data->items = tmp;
|
||||
item1->ot_data->len = len;
|
||||
/* Add back the offset of item2 to the newly created */
|
||||
if (item2->ot_data->offset > 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < (int) item2->ot_data->len ; i++, itr++)
|
||||
{
|
||||
/* This must be > 0, just because this is how it works */
|
||||
itr->source_cluster += item2->ot_data->offset -
|
||||
item1->ot_data->offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EAPI Eina_Bool
|
||||
evas_common_font_ot_populate_text_props(void *_fn, const Eina_Unicode *text,
|
||||
Evas_Text_Props *props, int len)
|
||||
|
|
|
@ -19,6 +19,8 @@ struct _Evas_Font_OT_Data
|
|||
int refcount;
|
||||
size_t len;
|
||||
Evas_Font_OT_Data_Item *items;
|
||||
size_t offset; /* The offset from the start of the script segment,
|
||||
this is useful when it's a split item */
|
||||
};
|
||||
# else
|
||||
typedef void *Evas_Font_OT_Data;
|
||||
|
@ -81,5 +83,8 @@ evas_common_font_ot_cutoff_text_props(Evas_Text_Props *props, int cutoff);
|
|||
|
||||
EAPI void
|
||||
evas_common_font_ot_split_text_props(Evas_Text_Props *base, Evas_Text_Props *ext, int cutoff);
|
||||
|
||||
EAPI void
|
||||
evas_common_font_ot_merge_text_props(Evas_Text_Props *item1, const Evas_Text_Props *item2);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -79,4 +79,14 @@ evas_common_text_props_split(Evas_Text_Props *base,
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Won't work in the middle of ligatures */
|
||||
EAPI void
|
||||
evas_common_text_props_merge(Evas_Text_Props *item1,
|
||||
const Evas_Text_Props *item2)
|
||||
{
|
||||
#ifdef OT_SUPPORT
|
||||
evas_common_font_ot_merge_text_props(item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,4 +35,6 @@ evas_common_text_props_cutoff(Evas_Text_Props *props, int cutoff);
|
|||
EAPI void
|
||||
evas_common_text_props_split(Evas_Text_Props *base, Evas_Text_Props *ext,
|
||||
int cutoff);
|
||||
EAPI void
|
||||
evas_common_text_props_merge(Evas_Text_Props *item1, const Evas_Text_Props *item2);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue