static int ft_cubic_to(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *to, void *user) { ft_outliner *ftol = (ft_outliner *)user; gf_path_add_cubic_to(ftol->path, INT2FIX(c1->x), INT2FIX(c1->y), INT2FIX(c2->x), INT2FIX(c2->y), INT2FIX(to->x), INT2FIX(to->y) ); if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) gf_path_close(ftol->path); return 0; }
/* TODO: update for elliptical arcs */ GF_EXPORT void gf_svg_path_build(GF_Path *path, GF_List *commands, GF_List *points) { u32 i, j, command_count; SVG_Point orig, ct_orig, ct_end, end, *tmp; command_count = gf_list_count(commands); orig.x = orig.y = ct_orig.x = ct_orig.y = 0; for (i=0, j=0; i<command_count; i++) { u8 *command = (u8 *)gf_list_get(commands, i); switch (*command) { case SVG_PATHCOMMAND_M: /* Move To */ tmp = (SVG_Point*)gf_list_get(points, j); orig = *tmp; gf_path_add_move_to(path, orig.x, orig.y); j++; /*provision for nextCurveTo when no curve is specified: "If there is no previous command or if the previous command was not an C, c, S or s, assume the first control point is coincident with the current point. */ ct_orig = orig; break; case SVG_PATHCOMMAND_L: /* Line To */ tmp = (SVG_Point*)gf_list_get(points, j); end = *tmp; gf_path_add_line_to(path, end.x, end.y); j++; orig = end; /*cf above*/ ct_orig = orig; break; case SVG_PATHCOMMAND_C: /* Curve To */ tmp = (SVG_Point*)gf_list_get(points, j); ct_orig = *tmp; tmp = (SVG_Point*)gf_list_get(points, j+1); ct_end = *tmp; tmp = (SVG_Point*)gf_list_get(points, j+2); end = *tmp; gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); ct_orig = ct_end; orig = end; j+=3; break; case SVG_PATHCOMMAND_S: /* Next Curve To */ ct_orig.x = 2*orig.x - ct_orig.x; ct_orig.y = 2*orig.y - ct_orig.y; tmp = (SVG_Point*)gf_list_get(points, j); ct_end = *tmp; tmp = (SVG_Point*)gf_list_get(points, j+1); end = *tmp; gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); ct_orig = ct_end; orig = end; j+=2; break; case SVG_PATHCOMMAND_Q: /* Quadratic Curve To */ tmp = (SVG_Point*)gf_list_get(points, j); ct_orig = *tmp; tmp = (SVG_Point*)gf_list_get(points, j+1); end = *tmp; gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y); orig = end; j+=2; break; case SVG_PATHCOMMAND_T: /* Next Quadratic Curve To */ ct_orig.x = 2*orig.x - ct_orig.x; ct_orig.y = 2*orig.y - ct_orig.y; tmp = (SVG_Point*)gf_list_get(points, j); end = *tmp; gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y); orig = end; j++; break; case SVG_PATHCOMMAND_Z: /* Close */ gf_path_close(path); break; } } }
GF_EXPORT GF_Err gf_path_add_cubic_to_vec(GF_Path *gp, GF_Point2D *c1, GF_Point2D *c2, GF_Point2D *pt) { return gf_path_add_cubic_to(gp, c1->x, c1->y, c2->x, c2->y, pt->x, pt->y); }
void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx) { M_Curve2D *c2D; SFVec2f orig, ct_orig, ct_end, end; u32 cur_index, i, remain, type_count, pt_count; SFVec2f *pts; M_Coordinate2D *coord; c2D = (M_Curve2D *)node; coord = (M_Coordinate2D *)c2D->point; drawable_reset_path(stack); if (!coord) return; stack->path->fineness = c2D->fineness; if (tr_state->visual->compositor->high_speed) stack->path->fineness /= 2; pts = coord->point.vals; if (!pts) return; cur_index = c2D->type.count ? 1 : 0; /*if the first type is a moveTo skip initial moveTo*/ i=0; if (cur_index) { while (c2D->type.vals[i]==0) i++; } ct_orig = orig = pts[ GET_IDX(i) ]; gf_path_add_move_to(stack->path, orig.x, orig.y); pt_count = coord->point.count; type_count = c2D->type.count; for (; i<type_count; i++) { switch (c2D->type.vals[i]) { /*moveTo, 1 point*/ case 0: CHECK_VALID_C2D(0); orig = pts[ GET_IDX(cur_index) ]; if (i) gf_path_add_move_to(stack->path, orig.x, orig.y); cur_index += 1; break; /*lineTo, 1 point*/ case 1: CHECK_VALID_C2D(0); end = pts[ GET_IDX(cur_index) ]; gf_path_add_line_to(stack->path, end.x, end.y); orig = end; cur_index += 1; break; /*curveTo, 3 points*/ case 2: CHECK_VALID_C2D(2); ct_orig = pts[ GET_IDX(cur_index) ]; ct_end = pts[ GET_IDX(cur_index+1) ]; end = pts[ GET_IDX(cur_index+2) ]; gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); cur_index += 3; ct_orig = ct_end; orig = end; break; /*nextCurveTo, 2 points (cf spec)*/ case 3: CHECK_VALID_C2D(1); ct_orig.x = 2*orig.x - ct_orig.x; ct_orig.y = 2*orig.y - ct_orig.y; ct_end = pts[ GET_IDX(cur_index) ]; end = pts[ GET_IDX(cur_index+1) ]; gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); cur_index += 2; ct_orig = ct_end; orig = end; break; /*all XCurve2D specific*/ /*CW and CCW ArcTo*/ case 4: case 5: CHECK_VALID_C2D(2); ct_orig = pts[ GET_IDX(cur_index) ]; ct_end = pts[ GET_IDX(cur_index+1) ]; end = pts[ GET_IDX(cur_index+2) ]; gf_path_add_arc_to(stack->path, end.x, end.y, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, (c2D->type.vals[i]==5) ? 1 : 0); cur_index += 3; ct_orig = ct_end; orig = end; break; /*ClosePath*/ case 6: gf_path_close(stack->path); break; /*quadratic CurveTo, 2 points*/ case 7: CHECK_VALID_C2D(1); ct_end = pts[ GET_IDX(cur_index) ]; end = pts[ GET_IDX(cur_index+1) ]; gf_path_add_quadratic_to(stack->path, ct_end.x, ct_end.y, end.x, end.y); cur_index += 2; ct_orig = ct_end; orig = end; break; } } /*what's left is an N-bezier spline*/ if (!idx && (pt_count > cur_index) ) { /*first moveto*/ if (!cur_index) cur_index++; remain = pt_count - cur_index; if (remain>1) gf_path_add_bezier(stack->path, &pts[cur_index], remain); } gf_node_dirty_clear(node, 0); drawable_mark_modified(stack, tr_state); }
static void build_shape(FSStack *st, GF_Node *node) { GF_FieldInfo field; MFVec2f *coords; MFInt32 *com, *widthIndex, *lineIndex, *fillIndex, *coordIndex; MFFloat *widths; MFColor *colors; u32 wi, li, fi, ci, command, i, has_ci; FSItem *fill_item, *line_item; Fixed w; SFVec2f cur, pt, ct1={0,0}, ct2, *pts; GF_Rect rc; u32 line_col, fill_col; Bool need_line, need_fill; /*get all fields*/ gf_node_get_field(node, 0, &field); coords = field.far_ptr; gf_node_get_field(node, 1, &field); com = field.far_ptr; gf_node_get_field(node, 2, &field); widths = field.far_ptr; gf_node_get_field(node, 3, &field); colors = field.far_ptr; gf_node_get_field(node, 4, &field); widthIndex = field.far_ptr; gf_node_get_field(node, 5, &field); lineIndex = field.far_ptr; gf_node_get_field(node, 6, &field); fillIndex = field.far_ptr; gf_node_get_field(node, 7, &field); coordIndex = field.far_ptr; wi = li = fi = ci = 0; w = 0; st->max_width = 0; cur.x = cur.y = 0; fill_item = line_item = NULL; need_line = need_fill = 0; cur.x = cur.y = 0; has_ci = coordIndex->count ? 1 : 0; pts = coords->vals; line_col = fill_col = 0; /*implicit commands: 0 1 2 3*/ /* if (widthIndex->count) { w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]]; if (!w) { need_line = 0; line_item = NULL; } else { need_line = 1; if (st->max_width<w) st->max_width = w; } wi++; } if (lineIndex->count) { if (w) { line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]); need_line = 1; } li++; } if (fillIndex->count) { if (fillIndex->vals[fi]==-1) { fill_col = 0; fill_item = NULL; } else { fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]); need_fill = 1; } fi++; } if (!coordIndex->count) return; cur = coords->vals[coordIndex->vals[ci]]; ci++; */ for (command=0; command<com->count; command++) { switch (com->vals[command]) { /*set line width*/ case 0: if (wi >= widthIndex->count) return; w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]]; if (!w) line_item = NULL; else { need_line = 1; if (st->max_width<w) st->max_width = w; } wi++; break; /*set line color*/ case 1: if (li > lineIndex->count) return; if (w) { line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]); need_line = 1; } li++; break; /*set fill color*/ case 2: if (fi >= fillIndex->count) return; if (fillIndex->vals[fi]==-1) { fill_col = 0; fill_item = NULL; } else { fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]); need_fill = 1; } fi++; break; /*moveTo*/ case 3: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); need_line = 0; } if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_move_to(fill_item->path, pt.x, pt.y); if (line_item) gf_path_add_move_to(line_item->path, pt.x, pt.y); ct1 = pt; cur = pt; ci++; break; /*lineTo*/ case 4: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_line_to(fill_item->path, pt.x, pt.y); if (line_item) gf_path_add_line_to(line_item->path, pt.x, pt.y); cur = pt; ci++; break; /*cubic curveTo*/ case 5: if ((has_ci && ci + 2 >= coordIndex->count) || (!has_ci && ci + 2>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { ct1 = pts[coordIndex->vals[ci]]; ct2 = pts[coordIndex->vals[ci+1]]; pt = pts[coordIndex->vals[ci+2]]; } else { ct1 = pts[ci]; ct2 = pts[ci+1]; pt = pts[ci+2]; } if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); ct1 = ct2; cur = pt; ci += 3; break; /*cubic nextCurveTo*/ case 6: if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } ct1.x = 2*cur.x - ct1.x; ct1.y = 2*cur.y - ct1.y; if (has_ci) { ct2 = pts[coordIndex->vals[ci]]; pt = pts[coordIndex->vals[ci+1]]; } else { ct2 = pts[ci]; pt = pts[ci+1]; } if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); ct1 = ct2; cur = pt; ci += 2; break; /*quadratic CurveTo*/ case 7: if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { ct1 = pts[coordIndex->vals[ci]]; pt = pts[coordIndex->vals[ci+1]]; } else { ct1 = pts[ci]; pt = pts[ci+1]; } if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y); if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y); cur = pt; ci += 2; break; /*quadratic nextCurveTo*/ case 8: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } ct1.x = 2*cur.x - ct1.x; ct1.y = 2*cur.y - ct1.y; if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y); if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y); cur = pt; ci += 2; break; /*close*/ case 9: if (fill_item) gf_path_close(fill_item->path); if (line_item) gf_path_close(line_item->path); break; } } /*compute bounds*/ st->bounds.width = st->bounds.height = 0; for (i=0; i<gf_list_count(st->items); i++) { line_item = gf_list_get(st->items, i); gf_path_get_bounds(line_item->path, &rc); gf_rect_union(&st->bounds, &rc); } }
static GF_Glyph *gdip_load_glyph(GF_FontReader *dr, u32 glyph_name) { GF_Rect bounds; GF_Glyph *glyph; GpPath *path_tmp; GpStringFormat *fmt; GpMatrix *mat; Float est_advance_h; unsigned short str[4]; int i; FontPriv *ctx = (FontPriv *)dr->udta; if (!ctx->font) return NULL; RectF rc; rc.X = rc.Y = 0; rc.Width = rc.Height = 0; GdipCreateStringFormat(StringFormatFlagsNoWrap | StringFormatFlagsNoFitBlackBox | StringFormatFlagsMeasureTrailingSpaces, LANG_NEUTRAL, &fmt); GdipSetStringFormatAlign(fmt, StringAlignmentNear); GdipCreatePath(FillModeAlternate, &path_tmp); if (glyph_name==0x20) { est_advance_h = ctx->whitespace_width; } else { /*to compute first glyph alignment (say 'x', we figure out its bounding full box by using the '_' char as wrapper (eg, "_x_") then the bounding box starting from xMin of the glyph ('x_'). The difference between both will give us a good approx of the glyph alignment*/ str[0] = glyph_name; str[1] = (unsigned short) '_'; str[2] = (unsigned short) 0; GdipAddPathString(path_tmp, (const WCHAR *)str, -1, ctx->font, ctx->font_style, ctx->em_size, &rc, fmt); GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); est_advance_h = rc.Width - ctx->underscore_width; } GdipResetPath(path_tmp); str[0] = glyph_name; str[1] = (unsigned short) 0; rc.X = rc.Y = 0; rc.Width = rc.Height = 0; GdipAddPathString(path_tmp, (const WCHAR *)str, -1, ctx->font, ctx->font_style, ctx->em_size, &rc, fmt); GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); /*flip so that we are in a font coordinate system - also move back the glyph to x=0 and y=baseline, GdiPlus doesn't do so*/ GdipCreateMatrix(&mat); GdipTranslateMatrix(mat, - rc.X, -ctx->ascent, MatrixOrderAppend); GdipScaleMatrix(mat, 1, -1, MatrixOrderAppend); GdipTransformPath(path_tmp, mat); GdipDeleteMatrix(mat); /*start enum*/ s32 count; GdipGetPointCount(path_tmp, &count); GpPointF *pts = new GpPointF[count]; BYTE *types = new BYTE[count]; GdipGetPathTypes(path_tmp, types, count); GdipGetPathPoints(path_tmp, pts, count); GF_SAFEALLOC(glyph, GF_Glyph); GF_SAFEALLOC(glyph->path, GF_Path); for (i=0; i<count; ) { BOOL closed = 0; s32 sub_type; sub_type = types[i] & PathPointTypePathTypeMask; if (sub_type == PathPointTypeStart) { gf_path_add_move_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y)); i++; } else if (sub_type == PathPointTypeLine) { gf_path_add_line_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y)); if (types[i] & PathPointTypeCloseSubpath) gf_path_close(glyph->path); i++; } else if (sub_type == PathPointTypeBezier) { assert(i+2<=count); gf_path_add_cubic_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y), FLT2FIX(pts[i+1].X), FLT2FIX(pts[i+1].Y), FLT2FIX(pts[i+2].X), FLT2FIX(pts[i+2].Y)); if (types[i+2] & PathPointTypeCloseSubpath) gf_path_close(glyph->path); i += 3; } else { assert(0); break; } } delete [] pts; delete [] types; GdipDeleteStringFormat(fmt); GdipDeletePath(path_tmp); glyph->ID = glyph_name; glyph->utf_name = glyph_name; glyph->vert_advance = (s32) (ctx->ascent-ctx->descent); glyph->horiz_advance = (s32) est_advance_h; gf_path_get_bounds(glyph->path, &bounds); glyph->width = FIX2INT(bounds.width); glyph->height = FIX2INT(bounds.height); return glyph; }