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
This commit is contained in:
Subhransu Mohanty 2016-11-28 11:21:33 -08:00 committed by Cedric BAIL
parent 6584c6cc27
commit 98b0408a4e
5 changed files with 794 additions and 1 deletions

View File

@ -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)

View File

@ -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);
}

View File

@ -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_

View File

@ -0,0 +1,527 @@
#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);
}
// 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);
}

View File

@ -0,0 +1,54 @@
#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);
/**
* 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_