textpath: improves text rendering quality of curved text 03/180203/5
authorYoungbok Shin <youngb.shin@samsung.com>
Fri, 25 May 2018 09:50:50 +0000 (18:50 +0900)
committerGerrit Code Review <gerrit@review.ap-northeast-2.compute.internal>
Tue, 29 May 2018 03:50:03 +0000 (03:50 +0000)
There was wrong logic for calculating # of slices, dt, dist of each segment.
It caused bad rendering quality by putting too much slices on small text.

In addition, textpath didn't care about smoothness of curve's slope changing.
The patch fixes to check differences of previous points and next points for Evas Map.
So, textpath can show smoothly curved text.

- Patch on EFL's upsteam:
https://phab.enlightenment.org/D6195

@tizen_fix

Change-Id: Ic03230504c400036acb480d22dacd59ed8a963cf

src/lib/efl_ui_textpath.c

index a1b547c..d145a0a 100644 (file)
@@ -48,6 +48,10 @@ struct _Efl_Ui_Textpath_Segment
      };
 };
 
+/* If you need to draw slices using Evas Line,
+ * define the following debug flag manually. */
+//#define EFL_UI_TEXTPATH_LINE_DEBUG
+
 struct _Efl_Ui_Textpath_Data
 {
    Evas_Object *text_obj;
@@ -65,6 +69,9 @@ struct _Efl_Ui_Textpath_Data
 
    Eina_Inlist *segments;
    int total_length;
+#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
+   Eina_List *lines;
+#endif
 };
 
 #define EFL_UI_TEXTPATH_DATA_GET(o, sd) \
@@ -77,25 +84,25 @@ _deg_to_rad(double angle)
 }
 
 static void
-_segment_draw(Efl_Ui_Textpath_Data *pd, int slice_no, int w1, int w2, int cmp, Evas_Map *map, Eina_Bezier bezier)
+_segment_draw(Efl_Ui_Textpath_Data *pd, int slice_no, double dt, double dist,
+              int w1, int cmp, Evas_Map *map, Eina_Bezier bezier,
+              int *last_x1, int *last_y1, int *last_x2, int *last_y2)
 {
    int x = 0, y = 0, w = 0, h = 0;
-   int i, len, seg_len;
+   int i;
    double u0, u1, v0, v1;
-   double dist, t, dt;
+   double t;
    double px, py, px2, py2;
    double rad;
    Eina_Vector2 vec, nvec, vec0, vec1, vec2, vec3;
    Eina_Matrix2 mat;
+#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
+   static Eina_Bool yello_color_flag = EINA_FALSE;
+   yello_color_flag = !yello_color_flag;
+#endif
 
-   len = w2 - w1;
    evas_object_geometry_get(pd->text_obj, NULL, NULL, &w, &h);
-   seg_len = eina_bezier_length_get(&bezier);
-   if (pd->autofit)
-     dt = len / (seg_len * (double) slice_no);
-   else
-     dt = 1.0 / (double) slice_no;
-   dist = len / (double)slice_no;
+
    rad = _deg_to_rad(90);
    eina_matrix2_values_set(&mat, cos(rad), -sin(rad), sin(rad), cos(rad));
 
@@ -117,19 +124,46 @@ _segment_draw(Efl_Ui_Textpath_Data *pd, int slice_no, int w1, int w2, int cmp, E
    vec2.x = (-vec.x + px);
    vec2.y = (-vec.y + py);
 
+   if (cmp == 0)
+     {
+        *last_x1 = (int) floor(vec1.x + x + 0.5);
+        *last_y1 = (int) floor(vec1.y + y + 0.5);
+        *last_x2 = (int) floor(vec2.x + x + 0.5);
+        *last_y2 = (int) floor(vec2.y + y + 0.5);
+     }
+
    //add points to map
    for (i = 0; i < slice_no; i++)
      {
+        int mp0_x, mp0_y;
+        int mp1_x, mp1_y;
+        int mp2_x, mp2_y;
+        int mp3_x, mp3_y;
+        double next_dt = dt;
+
         //v0, v3
         vec0.x = vec1.x;
         vec0.y = vec1.y;
         vec3.x = vec2.x;
         vec3.y = vec2.y;
 
+        //UV
+        u0 = w1 + i * dist;
+        u1 = u0 + dist;
+        if (u1 > w)
+          u1 = w;
+        v0 = (double) 0;
+        v1 = (double) h;
+
+        /* If u1 is modified not to exceed its end,
+         * modify next_dt according to changes of dist. */
+        if (u1 < u0 + dist)
+          next_dt = dt * (u1 - u0) / dist;
+
         //v1, v2
-        t = ((double) (i + 1) * dt);
+        t = (double) (i * dt) + next_dt;
         eina_bezier_point_at(&bezier, t, &px, &py);
-        eina_bezier_point_at(&bezier, t + dt, &px2, &py2);
+        eina_bezier_point_at(&bezier, t + next_dt, &px2, &py2);
 
         vec.x = (px2 - px);
         vec.y = (py2 - py);
@@ -143,21 +177,79 @@ _segment_draw(Efl_Ui_Textpath_Data *pd, int slice_no, int w1, int w2, int cmp, E
         vec2.x = (-vec.x + px);
         vec2.y = (-vec.y + py);
 
-        evas_map_point_coord_set(map, cmp + i * 4, (int) vec0.x + x, (int) vec0.y + y, 0);
-        evas_map_point_coord_set(map, cmp + i * 4 + 1, (int) vec1.x + x, (int) vec1.y + y, 0);
-        evas_map_point_coord_set(map, cmp + i * 4 + 2, (int) vec2.x + x, (int) vec2.y + y, 0);
-        evas_map_point_coord_set(map, cmp + i * 4 + 3, (int) vec3.x + x, (int) vec3.y + y, 0);
-
-        //UV
-        u0 = w1 + i * dist;
-        u1 = u0 + dist;
-        v0 = (double) 0;
-        v1 = (double) h;
+        /* Set mp1, mp2 position according to difference between previous points and next points.
+         * It improves smoothness of curve's slope changing. */
+        mp0_x = *last_x1;
+        mp0_y = *last_y1;
+        mp1_x = *last_x1 + (int) floor(vec1.x - vec0.x + 0.5);
+        mp1_y = *last_y1 + (int) floor(vec1.y - vec0.y + 0.5);
+        mp2_x = *last_x2 + (int) floor(vec2.x - vec3.x + 0.5);
+        mp2_y = *last_y2 + (int) floor(vec2.y - vec3.y + 0.5);
+        mp3_x = *last_x2;
+        mp3_y = *last_y2;
+
+        evas_map_point_coord_set(map, cmp + i * 4, mp0_x, mp0_y, 0);
+        evas_map_point_coord_set(map, cmp + i * 4 + 1, mp1_x, mp1_y, 0);
+        evas_map_point_coord_set(map, cmp + i * 4 + 2, mp2_x, mp2_y, 0);
+        evas_map_point_coord_set(map, cmp + i * 4 + 3, mp3_x, mp3_y, 0);
 
         evas_map_point_image_uv_set(map, cmp + i * 4, u0, v0);
         evas_map_point_image_uv_set(map, cmp + i * 4 + 1, u1, v0);
         evas_map_point_image_uv_set(map, cmp + i * 4 + 2, u1, v1);
         evas_map_point_image_uv_set(map, cmp + i * 4 + 3, u0, v1);
+
+        *last_x1 = mp1_x;
+        *last_y1 = mp1_y;
+        *last_x2 = mp2_x;
+        *last_y2 = mp2_y;
+
+#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
+        Evas_Object *line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
+        pd->lines = eina_list_append(pd->lines, line);
+        if (yello_color_flag)
+          evas_object_color_set(line, 255, 255, 0, 255);
+        else
+          evas_object_color_set(line, 255, 0, 0, 255);
+        evas_object_line_xy_set(line,
+                                mp0_x, mp0_y,
+                                mp1_x, mp1_y);
+        evas_object_show(line);
+
+        line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
+        pd->lines = eina_list_append(pd->lines, line);
+        if (yello_color_flag)
+          evas_object_color_set(line, 255, 255, 0, 255);
+        else
+          evas_object_color_set(line, 255, 0, 0, 255);
+        evas_object_line_xy_set(line,
+                                mp1_x, mp1_y,
+                                mp2_x, mp2_y);
+        evas_object_show(line);
+
+        line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
+        pd->lines = eina_list_append(pd->lines, line);
+        if (yello_color_flag)
+          evas_object_color_set(line, 255, 255, 0, 255);
+        else
+          evas_object_color_set(line, 255, 0, 0, 255);
+        evas_object_line_xy_set(line,
+                                mp2_x, mp2_y,
+                                mp3_x, mp3_y);
+        evas_object_show(line);
+
+        line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
+        pd->lines = eina_list_append(pd->lines, line);
+        if (yello_color_flag)
+          evas_object_color_set(line, 255, 255, 0, 255);
+        else
+          evas_object_color_set(line, 255, 0, 0, 255);
+        evas_object_line_xy_set(line,
+                                mp3_x, mp3_y,
+                                mp0_x, mp0_y);
+        evas_object_show(line);
+#endif
+
+        if (u1 >= w) break;
      }
 }
 
@@ -214,7 +306,8 @@ _map_point_calc(Efl_Ui_Textpath_Data *pd)
           }
         else if (seg->type == EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO)
           {
-             int no = pd->slice_no * seg->length / (double)pd->total_length;
+             int no = (int)ceil(pd->slice_no * seg->length / (double)pd->total_length);
+             if (no == 0) no = 1;
              map_no += no;
           }
      }
@@ -228,26 +321,22 @@ _text_draw(Efl_Ui_Textpath_Data *pd)
 {
    Efl_Ui_Textpath_Segment *seg;
    Evas_Map *map;
-   double slice_unit, slice_len;
    int w, h, w1, w2;
    int remained_w;
-   int total_slice, drawn_slice = 0;
    int cur_map_point = 0, map_point_no;
+   int last_x1, last_y1, last_x2, last_y2;
+
+   last_x1 = last_y1 = last_x2 = last_y2 = 0;
 
    evas_object_geometry_get(pd->text_obj, NULL, NULL, &w, &h);
    if (pd->autofit)
      remained_w = w;
    else
      remained_w = pd->total_length;
-   slice_unit = (double)pd->slice_no / pd->total_length;
-
-   slice_len = 1.0 / slice_unit;
-   total_slice = w / slice_len + 1;
 
    map_point_no = _map_point_calc(pd);
    if (map_point_no == 0)
      {
-        ERR("no map point");
         evas_object_map_enable_set(pd->text_obj, EINA_FALSE);
         return;
      }
@@ -256,6 +345,12 @@ _text_draw(Efl_Ui_Textpath_Data *pd)
    evas_map_util_object_move_sync_set(map, EINA_TRUE);
    /* END */
 
+#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
+   Evas_Object *line;
+   EINA_LIST_FREE(pd->lines, line)
+      evas_object_del(line);
+#endif
+
    w1 = w2 = 0;
    EINA_INLIST_FOREACH(pd->segments, seg)
      {
@@ -269,24 +364,33 @@ _text_draw(Efl_Ui_Textpath_Data *pd)
           w2 = w;
         if (seg->type == EFL_GFX_PATH_COMMAND_TYPE_LINE_TO)
           {
-             drawn_slice += 1;
              _text_on_line_draw(pd, w1, w2, cur_map_point, map, seg->line);
              cur_map_point += 4;
           }
         else
           {
+             double slice_value, dt, dist;
              int slice_no;
-             slice_no = pd->slice_no * seg->length / (double)pd->total_length;
-             if (slice_no == 0)
-               slice_no = len * slice_unit + 1;
-             if (remained_w == 0)
-               slice_no = total_slice - drawn_slice;
-             drawn_slice += slice_no;
-             _segment_draw(pd, slice_no, w1, w2, cur_map_point, map, seg->bezier);
+
+             slice_value = pd->slice_no * seg->length / (double)pd->total_length;
+             dt = (double)pd->total_length / (pd->slice_no * seg->length);
+
+             if (pd->autofit)
+               dist = (double)pd->total_length / (double)pd->slice_no;
+             else
+               dist = (double)pd->total_length * (w2 - w1) / ((double)pd->slice_no * seg->length);
+
+             slice_no = (int)ceil(slice_value);
+             dt = (double)slice_value * dt / (double)slice_no;
+             dist = (double)slice_value * dist / (double)slice_no;
+
+             _segment_draw(pd, slice_no, dt, dist,
+                           w1, cur_map_point, map, seg->bezier,
+                           &last_x1, &last_y1, &last_x2, &last_y2);
              cur_map_point += slice_no * 4;
           }
         w1 = w2;
-        remained_w -= len;
+        remained_w -= seg->length;
      }
    evas_object_map_enable_set(pd->text_obj, EINA_TRUE);
    evas_object_map_set(pd->text_obj, map);
@@ -561,6 +665,12 @@ _efl_ui_textpath_eo_base_destructor(Eo *obj, Efl_Ui_Textpath_Data *pd)
         free(seg);
      }
 
+#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
+   Evas_Object *line;
+   EINA_LIST_FREE(pd->lines, line)
+      evas_object_del(line);
+#endif
+
    //efl_destructor(efl_super(obj, MY_CLASS));
    eo_do_super(obj, MY_CLASS, eo_destructor());
 }