static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size, BOOL todo_size) { BYTE * types; INT size, idx = 0, eidx = 0, numskip; GpPointF * points; char ename[POINT_TYPE_MAX_LEN], name[POINT_TYPE_MAX_LEN]; if(GdipGetPointCount(path, &size) != Ok){ skip("Cannot perform path comparisons due to failure to retrieve path.\n"); return; } if(todo_size) todo_wine ok(size == expected_size, "Path size %d does not match expected size %d\n", size, expected_size); else ok(size == expected_size, "Path size %d does not match expected size %d\n", size, expected_size); points = HeapAlloc(GetProcessHeap(), 0, size * sizeof(GpPointF)); types = HeapAlloc(GetProcessHeap(), 0, size); if(GdipGetPathPoints(path, points, size) != Ok || GdipGetPathTypes(path, types, size) != Ok){ skip("Cannot perform path comparisons due to failure to retrieve path.\n"); goto end; } numskip = expected_size ? expected[eidx].wine_only_entries_preceding : 0; while (idx < size && eidx < expected_size){ /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in * floating point to integer conversion */ BOOL match = (types[idx] == expected[eidx].type) && fabs(points[idx].X - expected[eidx].X) <= 2.0 && fabs(points[idx].Y - expected[eidx].Y) <= 2.0; stringify_point_type(expected[eidx].type, ename); stringify_point_type(types[idx], name); if (expected[eidx].todo || numskip) todo_wine ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx, ename, expected[eidx].X, expected[eidx].Y, name, points[idx].X, points[idx].Y); else ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx, ename, expected[eidx].X, expected[eidx].Y, name, points[idx].X, points[idx].Y); if (match || expected[eidx].todo != 2) idx++; if (match || !numskip--) numskip = expected[++eidx].wine_only_entries_preceding; } end: HeapFree(GetProcessHeap(), 0, types); HeapFree(GetProcessHeap(), 0, points); }
GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count) { GpStatus ret; GpPointF *ptf; INT i; if(count <= 0) return InvalidParameter; ptf = GdipAlloc(sizeof(GpPointF)*count); if(!ptf) return OutOfMemory; ret = GdipGetPathPoints(path,ptf,count); if(ret == Ok) for(i = 0;i < count;i++){ points[i].X = roundr(ptf[i].X); points[i].Y = roundr(ptf[i].Y); }; GdipFree(ptf); return ret; }
/***************************************************************************** * GdipCreateRegionPath [GDIPLUS.@] * * Creates a GpRegion from a GpPath * * PARAMS * path [I] path to base the region on * region [O] pointer to the newly allocated region * * RETURNS * SUCCESS: Ok * FAILURE: InvalidParameter * * NOTES * If a path has no floating point points, its points will be stored as shorts * (INTPATH) * * If a path is empty, it is considered to be an INTPATH */ GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region) { region_element* element; GpPoint *pointsi; GpPointF *pointsf; GpStatus stat; DWORD flags = FLAGS_INTPATH; INT count, i; TRACE("%p, %p\n", path, region); if (!(path && region)) return InvalidParameter; *region = GdipAlloc(sizeof(GpRegion)); if(!*region) return OutOfMemory; stat = init_region(*region, RegionDataPath); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } element = &(*region)->node; count = path->pathdata.Count; /* Test to see if the path is an Integer path */ if (count) { pointsi = GdipAlloc(sizeof(GpPoint) * count); pointsf = GdipAlloc(sizeof(GpPointF) * count); if (!(pointsi && pointsf)) { GdipFree(pointsi); GdipFree(pointsf); GdipDeleteRegion(*region); return OutOfMemory; } stat = GdipGetPathPointsI(path, pointsi, count); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } stat = GdipGetPathPoints(path, pointsf, count); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } for (i = 0; i < count; i++) { if (!(pointsi[i].X == pointsf[i].X && pointsi[i].Y == pointsf[i].Y )) { flags = FLAGS_NOFLAGS; break; } } GdipFree(pointsi); GdipFree(pointsf); } stat = GdipClonePath(path, &element->elementdata.pathdata.path); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } /* 3 for headers, once again size doesn't count itself */ element->elementdata.pathdata.pathheader.size = ((sizeof(DWORD) * 3)); switch(flags) { /* Floats, sent out as floats */ case FLAGS_NOFLAGS: element->elementdata.pathdata.pathheader.size += (sizeof(DWORD) * count * 2); break; /* INTs, sent out as packed shorts */ case FLAGS_INTPATH: element->elementdata.pathdata.pathheader.size += (sizeof(DWORD) * count); break; default: FIXME("Unhandled flags (%08x). Expect wrong results.\n", flags); } element->elementdata.pathdata.pathheader.size += get_pathtypes_size(path); element->elementdata.pathdata.pathheader.magic = VERSION_MAGIC; element->elementdata.pathdata.pathheader.count = count; element->elementdata.pathdata.pathheader.flags = flags; (*region)->header.size = sizeheader_size + get_element_size(element); return Ok; }
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; }
static M4Err gdip_add_text_to_path(FontRaster *dr, LPM4PATH path, Bool flipText, const unsigned short* string, Float left, Float top, Float x_scaling, Float y_scaling, Float ascent, M4Rect *bounds) { GpPath *path_tmp; GpMatrix *mat; GpStringFormat *fmt; Float real_start; unsigned short str[4]; FontPriv *ctx = (FontPriv *)dr->priv; if (!ctx->font) return M4BadParam; RectF rc; rc.X = rc.Y = 0; rc.Width = rc.Height = 0; /*find first non-space char and estimate its glyph pos since GDIplus doesn't give this info*/ s32 len = utf8_wcslen(string); s32 i=0; for (; i<len; i++) { if (string[i] != (unsigned short) ' ') break; } if (i>=len) return M4OK; GdipCreateStringFormat(StringFormatFlagsNoWrap | StringFormatFlagsNoFitBlackBox | StringFormatFlagsMeasureTrailingSpaces, LANG_NEUTRAL, &fmt); GdipSetStringFormatAlign(fmt, StringAlignmentNear); GdipCreatePath(FillModeAlternate, &path_tmp); /*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] = (unsigned short) '_'; str[1] = string[i]; str[2] = (unsigned short) '_'; str[3] = (unsigned short) 0; GdipAddPathString(path_tmp, str, -1, ctx->font, ctx->font_style, ctx->font_size, &rc, fmt); GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); Float w1 = rc.Width - 2 * ctx->underscore_width; GdipResetPath(path_tmp); str[0] = string[i]; str[1] = (unsigned short) '_'; str[2] = (unsigned short) 0; rc.X = rc.Y = 0; rc.Width = rc.Height = 0; GdipAddPathString(path_tmp, str, -1, ctx->font, ctx->font_style, ctx->font_size, &rc, fmt); GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); real_start = w1 - (rc.Width - ctx->underscore_width); GdipResetPath(path_tmp); rc.X = rc.Y = 0; rc.Width = rc.Height = 0; GdipAddPathString(path_tmp, string, -1, ctx->font, ctx->font_style, ctx->font_size, &rc, fmt); GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); GdipCreateMatrix(&mat); Float off_left = rc.X; Float off_left_real = rc.X; /*adjust all white space at begin*/ adjust_white_space(string, &off_left, -1*ctx->whitespace_width); if (flipText) { /*first translate in local system*/ GdipTranslateMatrix(mat, left - off_left + real_start, -top, MatrixOrderAppend); /*then scale as specified*/ GdipScaleMatrix(mat, x_scaling, -y_scaling, MatrixOrderAppend); } else { /*first translate in local system*/ GdipTranslateMatrix(mat, left - off_left + real_start, top-ascent, MatrixOrderAppend); /*then scale as specified*/ GdipScaleMatrix(mat, x_scaling, y_scaling, MatrixOrderAppend); } GdipTransformPath(path_tmp, 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); for (i=0; i<count; ) { BOOL closed = 0; s32 sub_type; sub_type = types[i] & PathPointTypePathTypeMask; if (sub_type == PathPointTypeStart) { m4_path_add_move_to(path, pts[i].X, pts[i].Y); i++; } else if (sub_type == PathPointTypeLine) { m4_path_add_line_to(path, pts[i].X, pts[i].Y); if (types[i] & PathPointTypeCloseSubpath) m4_path_close(path); i++; } else if (sub_type == PathPointTypeBezier) { assert(i+2<=count); m4_path_add_cubic_to(path, pts[i].X, pts[i].Y, pts[i+1].X, pts[i+1].Y, pts[i+2].X, pts[i+2].Y); if (types[i+2] & PathPointTypeCloseSubpath) m4_path_close(path); i += 3; } else { assert(0); break; } } delete [] pts; delete [] types; GdipResetPath(path_tmp); adjust_white_space(string, &rc.Width, ctx->whitespace_width); rc.X = off_left_real; rc.X = off_left; /*special case where string is just space*/ if (!rc.Height) rc.Height = 1; GdipAddPathRectangles(path_tmp, &rc, 1); GdipGetPathWorldBounds(path_tmp, &rc, mat, NULL); bounds->x = rc.X; bounds->y = rc.Y; bounds->width = rc.Width; bounds->height = rc.Height; GdipDeleteStringFormat(fmt); GdipDeletePath(path_tmp); GdipDeleteMatrix(mat); return M4OK; }