📄 path2d_stroker.c
字号:
point += 2; tags += 2; vec1 = point[-2]; vec2 = point[-1]; if ( point <= limit ) { GF_Point2D vec; vec = point[0]; error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); if ( error ) goto Exit; continue; } error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); goto Close; } break; } }Close: if ( error ) goto Exit; error = FT_Stroker_EndSubPath(stroker, (outline->tags[outline->contours[n]]==GF_PATH_CLOSE) ? 1 : 0); if ( error ) goto Exit; first = last + 1; } return 0;Exit: return error;Invalid_Outline: return -1;}#define GF_PATH_DOT_LEN 1#define GF_PATH_DOT_SPACE 2#define GF_PATH_DASH_LEN 3static Fixed gf_path_get_dash(GF_PenSettings *pen, u32 dash_slot, u32 *next_slot){ Fixed ret = 0; switch (pen->dash) { case GF_DASH_STYLE_DOT: if (dash_slot==0) ret = GF_PATH_DOT_LEN; else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; *next_slot = (dash_slot + 1) % 2; return ret * pen->width; case GF_DASH_STYLE_DASH: if (dash_slot==0) ret = GF_PATH_DASH_LEN; else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; *next_slot = (dash_slot + 1) % 2; return ret * pen->width; case GF_DASH_STYLE_DASH_DOT: if (dash_slot==0) ret = GF_PATH_DASH_LEN; else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; else if (dash_slot==2) ret = GF_PATH_DOT_LEN; else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; *next_slot = (dash_slot + 1) % 4; return ret * pen->width; case GF_DASH_STYLE_DASH_DASH_DOT: if (dash_slot==0) ret = GF_PATH_DASH_LEN; else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; else if (dash_slot==2) ret = GF_PATH_DASH_LEN; else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; else if (dash_slot==4) ret = GF_PATH_DOT_LEN; else if (dash_slot==5) ret = GF_PATH_DOT_SPACE; *next_slot = (dash_slot + 1) % 6; return ret * pen->width; case GF_DASH_STYLE_DASH_DOT_DOT: if (dash_slot==0) ret = GF_PATH_DASH_LEN; else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; else if (dash_slot==2) ret = GF_PATH_DOT_LEN; else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; else if (dash_slot==4) ret = GF_PATH_DOT_LEN; else if (dash_slot==5) ret = GF_PATH_DOT_SPACE; *next_slot = (dash_slot + 1) % 6; return ret * pen->width; case GF_DASH_STYLE_CUSTOM: case GF_DASH_STYLE_CUSTOM_ABS: if (!pen->dash_set || !pen->dash_set->num_dash) return 0; if (dash_slot>=pen->dash_set->num_dash) dash_slot = 0; ret = pen->dash_set->dashes[dash_slot]; *next_slot = (1 + dash_slot) % pen->dash_set->num_dash; if (pen->dash==GF_DASH_STYLE_CUSTOM_ABS) return ret; /*custom dashes are of type Fixed !!*/ return gf_mulfix(ret, pen->width); default: case GF_DASH_STYLE_PLAIN: *next_slot = 0; return 0; }}/* Credits go to Raph Levien for libart / art_vpath_dash *//* FIXEME - NOT DONE - Merge first and last subpaths when first and last dash segment are joined a closepath. */static GF_Err gf_path_mergedashes(GF_Path *gp, u32 start_contour_index){ u32 i, dash_first_pt, dash_nb_pts; if (start_contour_index) { dash_nb_pts = gp->contours[start_contour_index] - gp->contours[start_contour_index-1]; dash_first_pt = gp->contours[start_contour_index-1]+1; } else { dash_nb_pts = gp->contours[start_contour_index]+1; dash_first_pt = 0; } /*skip first point of first dash in subpath (same as last point of last dash)*/ for (i=1;i<dash_nb_pts; i++) { GF_Err e = gf_path_add_line_to_vec(gp, &gp->points[dash_first_pt + i]); if (e) return e; } /*remove initial dash*/ gp->n_points -= dash_nb_pts; memmove(gp->points + dash_first_pt, gp->points + dash_first_pt + dash_nb_pts, sizeof(GF_Point2D)*(gp->n_points - dash_first_pt)); memmove(gp->tags + dash_first_pt, gp->tags + dash_first_pt + dash_nb_pts, sizeof(u8)*(gp->n_points - dash_first_pt)); for (i=start_contour_index; i<gp->n_contours-1; i++) { gp->contours[i] = gp->contours[i+1] - dash_nb_pts; } gp->n_contours--; gp->contours = (u32 *)realloc(gp->contours, sizeof(u32)*gp->n_contours);/* gp->points = realloc(gp->points, sizeof(GF_Point2D)*gp->n_points); gp->tags = realloc(gp->tags, sizeof(u8)*gp->n_points); gp->n_alloc_points = gp->n_points;*/ return GF_OK;}static GF_Err evg_dash_subpath(GF_Path *dashed, GF_Point2D *pts, u32 nb_pts, GF_PenSettings *pen, Fixed length_scale){ Fixed *dists; Fixed totaldist; Fixed dash; Fixed dist; s32 offsetinit; u32 next_offset; s32 toggleinit; s32 firstindex; Bool toggle_check; GF_Err e; u32 i, start_ind; Fixed phase; s32 offset, toggle; dists = (Fixed *)malloc(sizeof (Fixed) * nb_pts); if (dists == NULL) return GF_OUT_OF_MEM; /* initial values */ toggleinit = 1; offsetinit = 0; dash = gf_path_get_dash(pen, offsetinit, &next_offset); if (length_scale) dash = gf_mulfix(dash, length_scale); firstindex = -1; toggle_check = 0; start_ind = 0; dist = 0; /* calculate line lengths and update offset*/ totaldist = 0; for (i = 0; i < nb_pts - 1; i++) { GF_Point2D diff; diff.x = pts[i+1].x - pts[i].x; diff.y = pts[i+1].y - pts[i].y; dists[i] = gf_v2d_len(&diff); if (pen->dash_offset > dists[i]) { pen->dash_offset -= dists[i]; dists[i] = 0; } else if (pen->dash_offset) { Fixed a, x, y, dx, dy; a = gf_divfix(pen->dash_offset, dists[i]); dx = pts[i + 1].x - pts[i].x; dy = pts[i + 1].y - pts[i].y; x = pts[i].x + gf_mulfix(a, dx); y = pts[i].y + gf_mulfix(a, dy); e = gf_path_add_move_to(dashed, x, y); if (e) goto err_exit; totaldist += dists[i]; dist = pen->dash_offset; pen->dash_offset = 0; start_ind = i; } else { totaldist += dists[i]; } } /* subpath fits within first dash and no offset*/ if (!dist && totaldist <= dash) { if (toggleinit) { gf_path_add_move_to_vec(dashed, &pts[0]); for (i=1; i<nb_pts; i++) { gf_path_add_line_to_vec(dashed, &pts[i]); } } free(dists); return GF_OK; } /* subpath is composed of at least one dash */ phase = 0; offset = offsetinit; toggle = toggleinit; i = start_ind; if (toggle && !dist) { e = gf_path_add_move_to_vec(dashed, &pts[i]); if (e) goto err_exit; firstindex = dashed->n_contours - 1; } while (i < nb_pts - 1) { /* dash boundary is next */ if (dists[i] - dist > dash - phase) { Fixed a, x, y, dx, dy; dist += dash - phase; a = gf_divfix(dist, dists[i]); dx = pts[i + 1].x - pts[i].x; dy = pts[i + 1].y - pts[i].y; x = pts[i].x + gf_mulfix(a, dx); y = pts[i].y + gf_mulfix(a, dy); if (!toggle_check || ((x != pts[i].x) || (y != pts[i].y))) { if (toggle) { e = gf_path_add_line_to(dashed, x, y); if (e) goto err_exit; } else { e = gf_path_add_move_to(dashed, x, y); if (e) goto err_exit; } } /* advance to next dash */ toggle = !toggle; phase = 0; offset = next_offset; dash = gf_path_get_dash(pen, offset, &next_offset); if (length_scale) dash = gf_mulfix(dash, length_scale); } /* end of line in subpath is next */ else { phase += dists[i] - dist; i ++; toggle_check = 0; dist = 0; if (toggle) { e = gf_path_add_line_to_vec(dashed, &pts[i]); if (e) goto err_exit; toggle_check = 1; if ( (firstindex>=0) && (i == (nb_pts - 1) && ((firstindex + 1) != (s32) start_ind ) )) { /*merge if closed path*/ if ((pts[0].x==pts[nb_pts-1].x) && (pts[0].y==pts[nb_pts-1].y)) { e = gf_path_mergedashes(dashed, firstindex); if (e) goto err_exit; } } } } }err_exit:// pen->dash_offset = dist; free(dists); return GF_OK;}static GF_Path *gf_path_dash(GF_Path *path, GF_PenSettings *pen){ u32 i, j, nb_pts; GF_Point2D *pts; Fixed length_scale = 0; Fixed dash_off = pen->dash_offset; GF_Path *dashed = gf_path_new(); /* calculate line lengths and update offset*/ if (pen->path_length) { Fixed totaldist = 0; nb_pts = 0; for (i=0; i<path->n_contours; i++) { pts = &path->points[nb_pts]; nb_pts = 1+path->contours[i] - nb_pts; for (j=0; j<nb_pts-1; j++) { GF_Point2D diff; diff.x = pts[j+1].x - pts[j].x; diff.y = pts[j+1].y - pts[j].y; totaldist += gf_v2d_len(&diff); } nb_pts = 1+path->contours[i]; } length_scale = gf_divfix(totaldist, pen->path_length); pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale); } nb_pts = 0; for (i=0; i<path->n_contours; i++) { pts = &path->points[nb_pts]; nb_pts = 1+path->contours[i] - nb_pts; evg_dash_subpath(dashed, pts, nb_pts, pen, length_scale); nb_pts = 1+path->contours[i];// if (length_scale) pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale); } pen->dash_offset = dash_off; dashed->flags |= GF_PATH_FILL_ZERO_NONZERO; return dashed;}GF_EXPORTGF_Path *gf_path_get_outline(GF_Path *path, GF_PenSettings pen){ s32 error; GF_Path *outline; GF_Path *dashed; GF_Path *scaled; FT_Stroker stroker; memset(&stroker, 0, sizeof(stroker)); stroker.borders[0].start = -1; stroker.borders[1].start = -1; stroker.line_cap = pen.cap; stroker.line_join = pen.join; stroker.miter_limit = pen.miterLimit; stroker.radius = pen.width/2; /*security: some SVG paths use a single MoveTo for points drawing but the stroker needs at least 2 points*/ if (path->n_points==1) gf_path_add_line_to(path, path->points[0].x, path->points[0].y); gf_path_flatten(path); scaled = NULL; /*if not centered, simply scale path...*/ if (pen.align) { Fixed sx, sy; GF_Rect bounds; gf_path_get_bounds(path, &bounds); if (pen.align==GF_PATH_LINE_OUTSIDE) { sx = gf_divfix(bounds.width+pen.width, bounds.width); sy = gf_divfix(bounds.height+pen.width, bounds.height); } else { /*note: this may result in negative scaling, not our pb but the author's one*/ sx = gf_divfix(bounds.width-pen.width, bounds.width); sy = gf_divfix(bounds.height-pen.width, bounds.height); } if (sx && sy) { u32 i; scaled = gf_path_clone(path); for (i=0; i<scaled->n_points; i++) { scaled->points[i].x = gf_mulfix(scaled->points[i].x, sx); scaled->points[i].y = gf_mulfix(scaled->points[i].y, sy); } path = scaled; } } /*if dashing, first flatten path then dash all segments*/ dashed = NULL; /*security, seen in some SVG files*/ if (pen.dash_set && (pen.dash_set->num_dash==1) && (pen.dash_set->dashes[0]==0)) pen.dash = GF_DASH_STYLE_PLAIN; if (pen.dash) { GF_Path *flat; flat = gf_path_get_flatten(path); if (!flat) return NULL; dashed = gf_path_dash(flat, &pen); gf_path_del(flat); if (!dashed) return NULL; path = dashed; } outline = NULL; error = FT_Stroker_ParseOutline(&stroker, path); if (!error) { u32 nb_pt, nb_cnt; error = FT_Stroker_GetCounts(&stroker, &nb_pt, &nb_cnt); if (!error) { FT_StrokeBorder sborder; outline = gf_path_new(); if (nb_pt) { outline->points = (GF_Point2D *) malloc(sizeof(GF_Point2D)*nb_pt); outline->tags = (u8 *) malloc(sizeof(u8)*nb_pt); outline->contours = (u32 *) malloc(sizeof(u32)*nb_cnt); outline->n_alloc_points = nb_pt; sborder = &stroker.borders[0]; if (sborder->valid ) ft_stroke_border_export(sborder, outline); sborder = &stroker.borders[1]; /*if left border is valid this is a closed path, used odd/even rule - we will have issues at recovering segments...*/ if (sborder->valid && sborder->num_points) { ft_stroke_border_export(sborder, outline); } /*otherwise this is an open path, use zero/non-zero*/ else { outline->flags |= GF_PATH_FILL_ZERO_NONZERO; } } outline->flags |= GF_PATH_BBOX_DIRTY; /*our caps are cubic bezier!!*/ if ( (path->flags & GF_PATH_FLATTENED) && (pen.cap!=GF_LINE_CAP_ROUND) && (pen.join!=GF_LINE_JOIN_ROUND) ) outline->flags |= GF_PATH_FLATTENED; } } if (stroker.borders[0].points) free(stroker.borders[0].points); if (stroker.borders[0].tags) free(stroker.borders[0].tags); if (stroker.borders[1].points) free(stroker.borders[1].points); if (stroker.borders[1].tags) free(stroker.borders[1].tags); if (dashed) gf_path_del(dashed); if (scaled) gf_path_del(scaled); return outline;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -