static_lib: Added triangulator library.
authorsubhransu mohanty <smohantty@gmail.com>
Tue, 22 Nov 2016 12:26:49 +0000 (21:26 +0900)
committerTaehyub Kim <taehyub.kim@samsung.com>
Wed, 30 Nov 2016 06:38:22 +0000 (15:38 +0900)
The Library breaks down shape in to list for triangles for opengl drawing.

Feature:
- shape outline triangulation using triangle strips.
- shape filling triangulation using curve flattning and  triangle fans.

Change-Id: I80db50b8415d0a3a44b01599ee3e024da4e7a630

src/static_libs/triangulator/triangulator_simple.c [new file with mode: 0644]
src/static_libs/triangulator/triangulator_simple.h [new file with mode: 0644]
src/static_libs/triangulator/triangulator_stroker.c [new file with mode: 0644]
src/static_libs/triangulator/triangulator_stroker.h [new file with mode: 0644]

diff --git a/src/static_libs/triangulator/triangulator_simple.c b/src/static_libs/triangulator/triangulator_simple.c
new file mode 100644 (file)
index 0000000..60f7114
--- /dev/null
@@ -0,0 +1,160 @@
+#include "triangulator_simple.h"
+
+Triangulator_Simple *
+triangulator_simple_new(void)
+{
+   Triangulator_Simple *st = calloc(1, sizeof(Triangulator_Simple));
+   st->vertices = eina_inarray_new(sizeof(float), 0);
+   st->stops = eina_inarray_new(sizeof(int), 0);
+   return st;
+}
+
+void
+triangulator_simple_free(Triangulator_Simple *st)
+{
+   eina_inarray_free(st->vertices);
+   eina_inarray_free(st->stops);
+}
+
+static void
+_add_line(Triangulator_Simple *st, const float x, const float y)
+{
+   float *ptr;
+
+   ptr = eina_inarray_grow(st->vertices, 2);
+   ptr[0] = x;
+   ptr[1] = y;
+
+   if (x > st->maxx)
+     st->maxx = x;
+   else if (x < st->minx)
+     st->minx = x;
+   if (y > st->maxy)
+     st->maxy = y;
+    else if (y < st->miny)
+      st->miny = y;
+}
+
+static void
+_calculate_centroid(const Efl_Gfx_Path_Command *cmds, const double *pts, double *cx, double *cy)
+{
+   double sumx = 0, sumy = 0;
+   int count = 0;
+
+   sumx += pts[0];
+   sumy += pts[1];
+   for (cmds++, count++, pts+=2; *cmds != EFL_GFX_PATH_COMMAND_TYPE_END; cmds++)
+     {
+        switch (*cmds)
+          {
+           case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO:
+              sumx += pts[0];
+              sumy += pts[1];
+              pts +=2;
+              count++;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO:
+              sumx += pts[0];
+              sumy += pts[1];
+              sumx += pts[2];
+              sumy += pts[3];
+              sumx += pts[4];
+              sumy += pts[5];
+              pts +=6;
+              count +=3;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO:
+           case EFL_GFX_PATH_COMMAND_TYPE_CLOSE:
+              *cx = sumx/count;
+              *cy = sumy/count;
+              return;
+           default:
+              break;
+          }
+      }
+   //
+   *cx = sumx/count;
+   *cy = sumy/count;
+}
+
+void
+triangulator_simple_process(Triangulator_Simple *st, const Efl_Gfx_Path_Command *cmds, const double *pts, Eina_Bool convex)
+{
+   double bw, bh, cx, cy, x, y, t, one_over_threshold_minus_1;
+   float *ptr;
+   int   *stop_ptr, threshold, i;
+   Eina_Bezier b;
+
+   eina_inarray_resize(st->vertices, 0);
+   eina_inarray_resize(st->stops, 0);
+   if (!convex)
+     {
+        _calculate_centroid(cmds, pts, &cx, &cy);
+        _add_line(st, cx, cy);
+     }
+
+   cx = pts[0];
+   cy = pts[1];
+   // The first element is always a moveTo
+   _add_line(st, cx, cy);
+   pts += 2;
+   cmds++;
+   for (; *cmds != EFL_GFX_PATH_COMMAND_TYPE_END; cmds++)
+     {
+        switch (*cmds)
+          {
+           case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO:
+
+              // add closing line for the previous contour
+              _add_line(st, cx, cy);
+
+              // update stop array
+              stop_ptr = eina_inarray_grow(st->stops, 1);
+              stop_ptr[0] = eina_inarray_count(st->vertices);
+
+              // add centroid if not convex
+              if (!convex)
+                {
+                   _calculate_centroid(cmds, pts, &cx, &cy);
+                   _add_line(st, cx, cy);
+                 }
+              cx = pts[0];
+              cy = pts[1];
+              _add_line(st, cx, cy);
+              pts += 2;
+              break;
+
+           case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO:
+
+              _add_line(st, pts[0], pts[1]);
+              pts += 2;
+              break;
+
+           case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO:
+              ptr = eina_inarray_nth(st->vertices, eina_inarray_count(st->vertices) - 2);
+              eina_bezier_values_set(&b, ptr[0], ptr[1], pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]);
+              eina_bezier_bounds_get(&b, NULL, NULL , &bw, &bh);
+              threshold = fminf(64, fmaxf(bw, bh) * 3.14f / 6);
+              if (threshold < 3) threshold = 3;
+              one_over_threshold_minus_1 = 1.0 / (threshold - 1);
+              for (i=1; i<threshold; ++i)
+                {
+                   t = i * one_over_threshold_minus_1;
+                   eina_bezier_point_at(&b, t, &x, &y);
+                   _add_line(st, x, y);
+                }
+              pts += 6;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_CLOSE:
+           case EFL_GFX_PATH_COMMAND_TYPE_LAST:
+           case EFL_GFX_PATH_COMMAND_TYPE_END:
+              break;
+          }
+     }
+   // add closing line for the previous contour
+   _add_line(st, cx, cy);
+
+   // update stop array
+   stop_ptr = eina_inarray_grow(st->stops, 1);
+   stop_ptr[0] = eina_inarray_count(st->vertices);
+}
\ No newline at end of file
diff --git a/src/static_libs/triangulator/triangulator_simple.h b/src/static_libs/triangulator/triangulator_simple.h
new file mode 100644 (file)
index 0000000..e87acc6
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef TRIANGULATOR_SIMPLE_H_
+#define TRIANGULATOR_SIMPLE_H_
+
+#include <Efl.h>
+
+typedef struct _Triangulator_Simple Triangulator_Simple;
+struct _Triangulator_Simple
+{
+   Eina_Inarray *vertices;
+   Eina_Inarray *stops;  //list of contours need to be drawn as separate triangle fan.
+   float minx;
+   float miny;
+   float maxx;
+   float maxy;
+};
+
+/**
+ * Creates a new simple triangulator.
+ *
+ */
+Triangulator_Simple * triangulator_simple_new(void);
+
+/**
+ * Frees the given triangulator and any associated resource.
+ *
+ * st The given triangulator.
+ */
+void triangulator_simple_free(Triangulator_Simple *st);
+
+/**
+ * Process the command list to generate triangle fans.
+ * The alogrithm handles multiple contours by providing the list of stops.
+ *
+ * cmds :        commnad list
+ * pts  :        point list.
+ * convex :      shape is convex or not.
+ *
+ * output: If the shape is convex then, the triangle fan will exactly fill the shape. but if its not convex, it will overflow
+ *         to outside shape, in that case user has to use stencil method (2 pass drawing) to fill the shape.
+ */
+void triangulator_simple_process(Triangulator_Simple *st, const Efl_Gfx_Path_Command *cmds, const double *pts, Eina_Bool convex);
+
+#endif // #endif // TRIANGULATOR_SIMPLE_H_
\ No newline at end of file
diff --git a/src/static_libs/triangulator/triangulator_stroker.c b/src/static_libs/triangulator/triangulator_stroker.c
new file mode 100644 (file)
index 0000000..69c4027
--- /dev/null
@@ -0,0 +1,542 @@
+
+#include "triangulator_stroker.h"
+#include <math.h>
+
+#define PI 3.1415
+#define CURVE_FLATNESS PI / 8
+
+Triangulator_Stroker *
+triangulator_stroker_new(void)
+{
+   Triangulator_Stroker *stroker = calloc(1, sizeof(Triangulator_Stroker));
+   stroker->vertices = eina_inarray_new(sizeof(float), 0);
+   stroker->arc_pts = eina_inarray_new(sizeof(float), 0);
+   stroker->miter_limit = 2;
+   return stroker;
+}
+
+void
+triangulator_stroker_free(Triangulator_Stroker *stroker)
+{
+   eina_inarray_free(stroker->vertices);
+   eina_inarray_free(stroker->arc_pts);
+}
+
+void triangulator_stroker_stroke_set(Triangulator_Stroker *stroker, float width,
+                                     Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style, Eina_Matrix3 *m)
+{
+   float scale_factor = 1.0;
+   if (m)
+     {
+        // get the minimum scale factor from matrix
+        scale_factor =  m->xx < m->yy ? m->xx : m->yy;
+     }
+   //@TODO devide the witdth/2 after fixing tizen_vector.c
+   stroker->width = (width * scale_factor);
+   stroker->join_style = join_style;
+   stroker->cap_style = cap_style;
+}
+
+// calculate the normal vector
+static void
+normal_vector(float x1, float y1, float x2, float y2, float width,
+              float *nx, float *ny)
+{
+   float pw;
+   float dx = x2 - x1;
+   float dy = y2 - y1;
+
+   if (dx == 0)
+     pw = width / fabsf(dy);
+   else if (dy == 0)
+     pw = width / fabsf(dx);
+   else
+     pw = width / sqrtf(dx*dx + dy*dy);
+
+   *nx = -dy * pw;
+   *ny = dx * pw;
+}
+
+// add a line segment
+static void
+add_line_segment(Triangulator_Stroker *stroker, float x, float y, float vx, float vy)
+{
+   float *ptr;
+
+   ptr = eina_inarray_grow(stroker->vertices, 4);
+   ptr[0] = x + vx;
+   ptr[1] = y + vy;
+   ptr[2] = x - vx;
+   ptr[3] = y - vy;
+}
+
+static void
+add_arc_points(Triangulator_Stroker *stroker, float cx, float cy, float from_x, float from_y, float to_x, float to_y)
+{
+   float tmp_x, tmp_y, *ptr;
+   float dx1 = from_x - cx;
+   float dy1 = from_y - cy;
+   float dx2 = to_x - cx;
+   float dy2 = to_y - cy;
+   int size;
+
+   eina_inarray_resize(stroker->arc_pts, 0);
+
+#define ADD_NEW_POINT                                             \
+   tmp_x = dx1 * stroker->cos_theta - dy1 * stroker->sin_theta;   \
+   tmp_y = dx1 * stroker->sin_theta + dy1 * stroker->cos_theta;   \
+   dx1 = tmp_x;                                                   \
+   dy1 = tmp_y;                                                   \
+   ptr = eina_inarray_grow(stroker->arc_pts, 2);                  \
+   ptr[0] = cx + dx1;                                             \
+   ptr[1] = cy + dy1;
+
+   // while more than 180 degrees left:
+   while (dx1 * dy2 - dx2 * dy1 < 0)
+     {
+        ADD_NEW_POINT
+     }
+
+   // while more than 90 degrees left:
+   while (dx1 * dx2 + dy1 * dy2 < 0)
+     {
+        ADD_NEW_POINT
+     }
+
+   // while more than 0 degrees left:
+   while (dx1 * dy2 - dx2 * dy1 > 0)
+     {
+        ADD_NEW_POINT
+     }
+
+   // remove last point which was rotated beyond [to_x, to_y].
+   size = eina_inarray_count(stroker->arc_pts);
+   if (size)
+     eina_inarray_resize(stroker->arc_pts, size - 2);
+}
+
+static void
+move_to(Triangulator_Stroker *stroker, const double *pts)
+{
+   float x2,y2, sx, sy, *ptr=NULL, *ptr1=NULL;
+   int pts_count, arc_pts_count, front, end, i=0;
+   Eina_Bool jump;
+
+   stroker->cx = pts[0];
+   stroker->cy = pts[1];
+   x2 = pts[2];
+   y2 = pts[3];
+   normal_vector(stroker->cx, stroker->cy, x2, y2, stroker->width, &stroker->nvx, &stroker->nvy);
+
+   // To acheive jumps we insert zero-area tringles. This is done by
+   // adding two identical points in both the end of previous strip
+   // and beginning of next strip
+   jump = eina_inarray_count(stroker->vertices);
+
+   switch (stroker->cap_style)
+     {
+      case EFL_GFX_CAP_BUTT:
+         if (jump)
+           {
+              ptr = eina_inarray_grow(stroker->vertices, 2);
+              ptr[0] = stroker->cx + stroker->nvx;
+              ptr[1] = stroker->cy + stroker->nvy;
+           }
+         break;
+      case EFL_GFX_CAP_SQUARE:
+         {
+            sx = stroker->cx - stroker->nvy;
+            sy = stroker->cy + stroker->nvx;
+            if (jump)
+              {
+                 ptr = eina_inarray_grow(stroker->vertices, 2);
+                 ptr[0] = sx + stroker->nvx;
+                 ptr[1] = sy + stroker->nvy;
+              }
+            add_line_segment(stroker, sx, sy, stroker->nvx, stroker->nvy);
+            break;
+         }
+      case EFL_GFX_CAP_ROUND:
+         {
+            add_arc_points(stroker, stroker->cx, stroker->cy,
+                           stroker->cx + stroker->nvx, stroker->cy + stroker->nvy,
+                           stroker->cx - stroker->nvx, stroker->cy - stroker->nvy);
+            arc_pts_count = eina_inarray_count(stroker->arc_pts);
+            front = 0;
+            end = arc_pts_count / 2;
+            if (arc_pts_count)
+              {
+                 eina_inarray_grow(stroker->vertices, eina_inarray_count(stroker->arc_pts) + 2 * jump);
+                 pts_count = eina_inarray_count(stroker->vertices);
+                 ptr1 = eina_inarray_nth(stroker->arc_pts, 0);
+                 ptr = eina_inarray_nth(stroker->vertices, 0);
+                 i = pts_count;
+              }
+            while (front != end)
+              {
+                 ptr[--i] = ptr1[2 * end - 1];
+                 ptr[--i] = ptr1[2 * end - 2];
+                 --end;
+                 if (front == end)
+                   break;
+                 ptr[--i] = ptr1[2 * front + 1];
+                 ptr[--i] = ptr1[2 * front + 0];
+                 ++front;
+              }
+
+            if (jump)
+              {
+                 ptr[i - 1] = ptr[i + 1];
+                 ptr[i - 2] = ptr[i + 0];
+              }
+            break;
+         }
+      default: break;
+     }
+   add_line_segment(stroker, stroker->cx, stroker->cy, stroker->nvx, stroker->nvy);
+}
+
+static void
+line_to(Triangulator_Stroker *stroker, const double *pts)
+{
+   add_line_segment(stroker, pts[0], pts[1], stroker->nvx, stroker->nvy);
+   stroker->cx = pts[0];
+   stroker->cy = pts[1];
+}
+
+static void
+cubic_to(Triangulator_Stroker *stroker, const double *pts)
+{
+   Eina_Bezier b;
+   float rad, vx, vy, cx, cy, threshold_minus_1, t;
+   double bw, bh, x, y;
+   int i, threshold;
+
+   eina_bezier_values_set(&b, stroker->cx, stroker->cy, pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]);
+   eina_bezier_bounds_get(&b, NULL, NULL, &bw, &bh);
+
+   rad = fmaxf(bw, bh);
+   threshold = fminf(64, (rad + stroker->curvyness_add) * stroker->curvyness_mul);
+   if (threshold < 4)
+     threshold = 4;
+   threshold_minus_1 = threshold - 1;
+   cx = stroker->cx;
+   cy = stroker->cy;
+
+   for (i = 1; i < threshold; ++i)
+     {
+        t = i / threshold_minus_1;
+        eina_bezier_point_at(&b, t, &x, &y);
+        normal_vector(cx, cy, x, y, stroker->width, &vx, &vy);
+        add_line_segment(stroker, x, y, vx, vy);
+        cx = x;
+        cy = y;
+     }
+
+   stroker->cx = cx;
+   stroker->cy = cy;
+
+   stroker->nvx = vx;
+   stroker->nvy = vy;
+}
+
+static void
+add_join(Triangulator_Stroker *stroker, float x , float y)
+{
+   int arc_pts_count, pts_count, i;
+   float prev_nvx, prev_nvy, xprod, px, py, qx, qy, pu, qv, ix, iy, *ptr;
+
+   // Creates a join to the next segment (cx, cy) -> (x, y)
+   normal_vector(stroker->cx, stroker->cy, x, y, stroker->width, &stroker->nvx, &stroker->nvy);
+
+   switch (stroker->join_style)
+     {
+      case EFL_GFX_JOIN_BEVEL:
+         break;
+      case EFL_GFX_JOIN_MITER:
+         {
+            // Find out on which side the join should be.
+            pts_count = eina_inarray_count(stroker->vertices);
+            ptr = eina_inarray_nth(stroker->vertices, pts_count - 2);
+            prev_nvx = ptr[0] - stroker->cx;
+            prev_nvy = ptr[1] - stroker->cy;
+            xprod = prev_nvx * stroker->nvy - prev_nvy * stroker->nvx;
+
+            // If the segments are parallel, use bevel join.
+            if (xprod < 0.001)
+              break;
+
+            // Find the corners of the previous and next segment to join.
+            if (xprod < 0)
+              {
+                 ptr = eina_inarray_nth(stroker->vertices, pts_count - 2);
+                 px = ptr[0];
+                 py = ptr[1];
+                 qx = stroker->cx - stroker->nvx;
+                 qy = stroker->cy - stroker->nvy;
+              }
+            else
+              {
+                 ptr = eina_inarray_nth(stroker->vertices, pts_count - 4);
+                 px = ptr[0];
+                 py = ptr[1];
+                 qx = stroker->cx + stroker->nvx;
+                 qy = stroker->cy - stroker->nvy;
+              }
+
+            // Find intersection point.
+            pu = px * prev_nvx + py * prev_nvy;
+            qv = qx * stroker->nvx + qy * stroker->nvy;
+            ix = (stroker->nvx * pu - prev_nvy * qv) / xprod;
+            iy = (prev_nvx * qv - stroker->nvx * pu) / xprod;
+
+            // Check that the distance to the intersection point is less than the miter limit.
+            if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= stroker->miter_limit * stroker->miter_limit)
+              {
+                 ptr = eina_inarray_grow(stroker->vertices, 4);
+                 ptr[0] = ix;
+                 ptr[1] = iy;
+                 ptr[2] = ix;
+                 ptr[3] = iy;
+              }
+            break;
+         }
+      case EFL_GFX_JOIN_ROUND:
+         {
+            pts_count = eina_inarray_count(stroker->vertices);
+            ptr = eina_inarray_nth(stroker->vertices, pts_count - 2);
+            prev_nvx = ptr[0] - stroker->cx;
+            prev_nvy = ptr[1] - stroker->cy;
+            if (stroker->nvx * prev_nvx - stroker->nvy * prev_nvy < 0)
+              {
+                 add_arc_points(stroker, 0, 0, stroker->nvx, stroker->nvy, -prev_nvx, -prev_nvy);
+                 arc_pts_count = eina_inarray_count(stroker->arc_pts);
+                 if (arc_pts_count)
+                   ptr = eina_inarray_nth(stroker->arc_pts, 0);
+                 for (i = arc_pts_count / 2; i > 0; --i)
+                   add_line_segment(stroker, stroker->cx, stroker->cy, ptr[2 * i - 2], ptr[2 * i - 1]);
+              }
+            else
+              {
+                 add_arc_points(stroker, 0, 0, -prev_nvx, -prev_nvy, stroker->nvx, stroker->nvy);
+                 arc_pts_count = eina_inarray_count(stroker->arc_pts) / 2;
+                 if (arc_pts_count)
+                   ptr = eina_inarray_nth(stroker->arc_pts, 0);
+                 for (i = 0; i < arc_pts_count / 2; ++i)
+                   add_line_segment(stroker, stroker->cx, stroker->cy, ptr[2 * i + 0], ptr[2 * i + 1]);
+              }
+            break;
+         }
+      default: break;
+     }
+   add_line_segment(stroker, stroker->cx, stroker->cy, stroker->nvx, stroker->nvy);
+}
+
+static void
+end_cap(Triangulator_Stroker *stroker)
+{
+   float *ptr, *ptr1;
+   int front, end, pts_count, arc_pts_count, i;
+
+   switch (stroker->cap_style)
+     {
+      case EFL_GFX_CAP_BUTT:
+         break;
+      case EFL_GFX_CAP_SQUARE:
+         add_line_segment(stroker, stroker->cx + stroker->nvy, stroker->cy - stroker->nvx, stroker->nvx, stroker->nvy);
+         break;
+      case EFL_GFX_CAP_ROUND:
+         {
+            pts_count = eina_inarray_count(stroker->vertices);
+            ptr = eina_inarray_nth(stroker->vertices, pts_count-4);
+            add_arc_points(stroker, stroker->cx, stroker->cy, ptr[2], ptr[3], ptr[0], ptr[1]);
+            arc_pts_count = eina_inarray_count(stroker->arc_pts);
+            if (arc_pts_count)
+              {
+                 ptr = eina_inarray_grow(stroker->vertices, arc_pts_count);
+                 ptr1 = eina_inarray_nth(stroker->arc_pts, 0);
+              }
+            front = 0;
+            end = arc_pts_count / 2;
+            i = 0;
+            while (front != end)
+              {
+                 ptr[i++] = ptr1[2 * end - 2];
+                 ptr[i++] = ptr1[2 * end - 1];
+                 --end;
+                 if (front == end)
+                   break;
+                 ptr[i++] = ptr1[2 * front + 0];
+                 ptr[i++] = ptr1[2 * front + 1];
+                 ++front;
+              }
+            break;
+         }
+      default: break;
+     }
+}
+
+static void
+_end_cap_or_join_closed(Triangulator_Stroker *stroker,
+                        const double *start,
+                        Eina_Bool implicit_close, Eina_Bool ends_at_start)
+{
+   int count;
+   float x, y, *ptr;
+
+   if (ends_at_start)
+     {
+        add_join(stroker, start[2], start[3]);
+     }
+   else if (implicit_close)
+     {
+        add_join(stroker, start[0], start[1]);
+        line_to(stroker, start);
+        add_join(stroker, start[2], start[3]);
+     }
+   else
+     {
+        end_cap(stroker);
+     }
+   // add the invisible triangle
+   count = eina_inarray_count(stroker->vertices);
+   ptr = eina_inarray_nth(stroker->vertices, 0);
+   x = ptr[count-2];
+   y = ptr[count-1];
+   ptr = eina_inarray_grow(stroker->vertices, 2);
+   ptr[0] = x;
+   ptr[1] = y;
+}
+
+static inline void
+_skip_duplicate_points(const double **pts, const double *end_pts)
+{
+   while ((*pts + 2) < end_pts && (*pts)[0] == (*pts)[2] &&
+          (*pts)[1] == (*pts)[3])
+     {
+        *pts += 2;
+     }
+}
+
+static void
+_path_info_get(const Efl_Gfx_Path_Command *cmds, const double *pts, Eina_Bool *implicit_close, Eina_Bool *ends_at_start)
+{
+   int i = 0;
+
+   *implicit_close = EINA_FALSE;
+   *ends_at_start = EINA_FALSE;
+   for (++cmds; *cmds != EFL_GFX_PATH_COMMAND_TYPE_END ; ++cmds)
+     {
+        switch (*cmds)
+          {
+           case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO:
+              i += 2;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO:
+              i += 6;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_CLOSE:
+              // this path has a implicit close
+              *implicit_close = EINA_TRUE;
+              // fall through
+           case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO:
+              if ((pts[0] == pts[i]) && (pts[1] == pts[i+1]))
+                *ends_at_start = EINA_TRUE;
+              return;
+           default:
+              break;
+          }
+     }
+   // this path is the last path with out implicit close.
+   *ends_at_start = pts[0] == pts[i] &&
+                    pts[1] == pts[i+1];
+}
+
+void
+triangulator_stroker_process(Triangulator_Stroker *stroker,
+                              const Efl_Gfx_Path_Command *cmds, const double *pts, int cmd_count, int pt_count)
+{
+   const double *end_pts = pts + pt_count;
+   const double *start_pts = 0;
+   Eina_Bool ends_at_start, implicit_close;
+   Efl_Gfx_Cap cap;
+   Efl_Gfx_Path_Command previous_type;
+
+   if (cmd_count < 2)
+     return;
+
+   eina_inarray_resize(stroker->vertices, 0);
+   stroker->curvyness_add = stroker->width;
+   stroker->curvyness_mul = CURVE_FLATNESS;
+   stroker->roundness = fmax(4, 2 * stroker->width * stroker->curvyness_mul);
+   // Over this level of segmentation, there doesn't seem to be any
+   // benefit, even for huge penWidth
+   if (stroker->roundness > 24)
+     stroker->roundness = 24;
+
+   stroker->sin_theta = sinf(PI / stroker->roundness);
+   stroker->cos_theta = cosf(PI / stroker->roundness);
+
+   cap = stroker->cap_style;
+   ends_at_start = EINA_FALSE;
+   implicit_close = EINA_FALSE;
+   previous_type = EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO;
+   for (; *cmds != EFL_GFX_PATH_COMMAND_TYPE_END; cmds++)
+     {
+        switch (*cmds)
+          {
+           case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO:
+              {
+                 if (previous_type != EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO)
+                   _end_cap_or_join_closed(stroker, start_pts, implicit_close, ends_at_start);
+
+                 // get the sub path deatils like closed path or start at end info.
+                 _path_info_get(cmds, pts, &implicit_close, &ends_at_start);
+
+                 start_pts = pts;
+                 _skip_duplicate_points(&start_pts, end_pts); // Skip duplicates to find correct normal.
+                 if (start_pts + 2 >= end_pts)
+                   return; // Nothing to see here...
+
+                 if (ends_at_start || implicit_close)
+                   stroker->cap_style = EFL_GFX_CAP_BUTT;
+
+                 move_to(stroker, start_pts);
+                 stroker->cap_style = cap;
+                 previous_type = EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO;
+                 pts+=2;
+                 break;
+              }
+           case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO:
+              if (stroker->cx != (float)pts[0] || stroker->cy != (float)pts[1])
+                {
+                   if (previous_type != EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO)
+                     add_join(stroker, pts[0], pts[1]);
+                   line_to(stroker, pts);
+                   previous_type = EFL_GFX_PATH_COMMAND_TYPE_LINE_TO;
+                }
+              pts+=2;
+              break;
+           case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO:
+              if (stroker->cx != (float)pts[0] || stroker->cy != (float)pts[1] ||
+                  (float)pts[0] != (float)pts[2] || (float)pts[1] != (float)pts[3] ||
+                  (float)pts[2] != (float)pts[4] || (float)pts[3] != (float)pts[5])
+                {
+                   if (stroker->cx != (float)pts[0] || stroker->cy != (float)pts[1])
+                     {
+                        if (previous_type != EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO)
+                          add_join(stroker, pts[0], pts[1]);
+                     }
+                   cubic_to(stroker, pts);
+                   previous_type = EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO;
+                }
+              pts+=6;
+              break;
+           default:
+              break;
+          }
+     }
+
+   if (previous_type != EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO)
+     _end_cap_or_join_closed(stroker, start_pts, implicit_close, ends_at_start);
+}
diff --git a/src/static_libs/triangulator/triangulator_stroker.h b/src/static_libs/triangulator/triangulator_stroker.h
new file mode 100644 (file)
index 0000000..56a6106
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef TRIANGULATOR_STROKER_H_
+#define TRIANGULATOR_STROKER_H_
+
+#include <Efl.h>
+
+typedef struct _Triangulator_Stroker Triangulator_Stroker;
+struct _Triangulator_Stroker
+{
+   Eina_Inarray *vertices;
+   Eina_Inarray *arc_pts;  // intermediate array for storing arc points
+   float cx, cy;           // current points
+   float nvx, nvy;         // normal vector
+   float width;
+   float miter_limit;
+
+   int roundness;            // Number of line segments in a round join
+   float sin_theta;          // sin(roundness / 360);
+   float cos_theta;          // cos(roundness / 360);
+   float curvyness_mul;
+   float curvyness_add;
+
+   Efl_Gfx_Join join_style;
+   Efl_Gfx_Cap  cap_style;
+};
+
+/**
+ * Creates a new triangulating stroker.
+ *
+ */
+Triangulator_Stroker *triangulator_stroker_new(void);
+
+/**
+ * Frees the given Stroker and any associated resource.
+ *
+ * stroker The given Stroker.
+ */
+void triangulator_stroker_free(Triangulator_Stroker *stroker);
+
+void triangulator_stroker_stroke_set(Triangulator_Stroker *stroker, float width,
+                                     Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style, Eina_Matrix3 *m);
+
+/**
+ * Process the command list to generate triangle strips.
+ * The alogrithm handles multiple contour by adding invisible triangles.
+ *
+ * cmds :        commnad list
+ * pts  :        point list.
+ * cmd_count :   number of commands.
+ * pt_count  :   number of points.
+ *
+ * output : It generates the outline in the form of triangle strips store in vertices array.
+ *          The array can be used to copy the data to a VBO and draw the data using TRIANGLE_STRIP.
+ */
+void triangulator_stroker_process(Triangulator_Stroker *stroker, const Efl_Gfx_Path_Command *cmds, const double *pts, int cmd_count, int pt_count);
+
+#endif // TRIANGULATOR_STROKER_H_
+