From 98b0408a4eed5c2beafd4f396b64ca54fb00270f Mon Sep 17 00:00:00 2001 From: Subhransu Mohanty Date: Mon, 28 Nov 2016 11:21:33 -0800 Subject: [PATCH] triangulator: add a static_lib for triangulation. idea is to keep all the algorithm for triangulation in one place 1. shape outline triangulation using triangle strips. 2. shape filling using curve flattning and polygon triangulation. Reviewers: Hermet, cedric Reviewed By: cedric Subscribers: raster, cedric, jpeg Differential Revision: https://phab.enlightenment.org/D3896 --- src/Makefile_Ector.am | 11 +- .../triangulator/triangulator_simple.c | 160 ++++++ .../triangulator/triangulator_simple.h | 43 ++ .../triangulator/triangulator_stroker.c | 527 ++++++++++++++++++ .../triangulator/triangulator_stroker.h | 54 ++ 5 files changed, 794 insertions(+), 1 deletion(-) create mode 100644 src/static_libs/triangulator/triangulator_simple.c create mode 100644 src/static_libs/triangulator/triangulator_simple.h create mode 100644 src/static_libs/triangulator/triangulator_stroker.c create mode 100644 src/static_libs/triangulator/triangulator_stroker.h diff --git a/src/Makefile_Ector.am b/src/Makefile_Ector.am index f8b76716b6..0f8a0a5339 100644 --- a/src/Makefile_Ector.am +++ b/src/Makefile_Ector.am @@ -108,6 +108,12 @@ static_libs/rg_etc/rg_etc2.c \ static_libs/rg_etc/rg_etc1.h \ static_libs/rg_etc/etc2_encoder.c +# Triangulator static lib +triangulator_sources = \ +static_libs/triangulator/triangulator_stroker.c \ +static_libs/triangulator/triangulator_simple.c \ +$(NULL) + # And the default software backend lib_ector_libector_la_SOURCES += \ lib/ector/software/ector_renderer_software_gradient_linear.c \ @@ -120,7 +126,8 @@ lib/ector/software/ector_software_surface.c \ lib/ector/software/ector_software_buffer.c \ static_libs/freetype/sw_ft_math.c \ static_libs/freetype/sw_ft_raster.c \ -static_libs/freetype/sw_ft_stroker.c +static_libs/freetype/sw_ft_stroker.c \ +$(triangulator_sources) # And now the gl backend lib_ector_libector_la_SOURCES += \ @@ -152,6 +159,7 @@ lib_ector_libector_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -I$(top_builddir)/src/lib/ector/gl \ -I$(top_srcdir)/src/static_libs/freetype \ -I$(top_srcdir)/src/static_libs/draw \ +-I$(top_srcdir)/src/static_libs/triangulator \ @ECTOR_CFLAGS@ \ -DPACKAGE_BIN_DIR=\"$(bindir)\" \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ @@ -204,3 +212,4 @@ static_libs/freetype/sw_ft_types.h \ static_libs/draw/draw.h \ static_libs/draw/draw_private.h \ $(ECTOR_GL_SHADERS_GEN) + diff --git a/src/static_libs/triangulator/triangulator_simple.c b/src/static_libs/triangulator/triangulator_simple.c new file mode 100644 index 0000000000..60f7114cdb --- /dev/null +++ b/src/static_libs/triangulator/triangulator_simple.c @@ -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; istops, 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 index 0000000000..e87acc6e40 --- /dev/null +++ b/src/static_libs/triangulator/triangulator_simple.h @@ -0,0 +1,43 @@ +#ifndef TRIANGULATOR_SIMPLE_H_ +#define TRIANGULATOR_SIMPLE_H_ + +#include + +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 index 0000000000..44f7b8946b --- /dev/null +++ b/src/static_libs/triangulator/triangulator_stroker.c @@ -0,0 +1,527 @@ + +#include "triangulator_stroker.h" +#include + +#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); +} + +// 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 index 0000000000..5cc7805638 --- /dev/null +++ b/src/static_libs/triangulator/triangulator_stroker.h @@ -0,0 +1,54 @@ +#ifndef TRIANGULATOR_STROKER_H_ +#define TRIANGULATOR_STROKER_H_ + +#include + +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); + +/** + * 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_ +