[PATCH v6 0/6] MR3288: gdiplus: Clip polygons before drawing them
Validate user data before passing it to PolyDraw. The program in the bug requests to draw figures outrageously outside the DC's region after presumably, some uninitialized values happen as a result of a missing font. Native gdiplus seems to handle this gracefully so we probably also should. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41617 Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> -- v6: gdiplus: Hook on optimized path drawing code gdiplus: Re-implement trace_path to avoid circular dependency gdiplus: Make get_region_hrgn public to gdiplus gdiplus: Clip polygons before drawing them gdiplus: Make get_graphis_device_bounds public to gdiplus gdiplus: Allow for writing nodes to an empty path points list https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> This should allow us to construct a path based on point that are all unknown at the time of list creation Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/graphicspath.c | 103 +++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 6cdc2a58059..4287845fa9b 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -75,10 +75,25 @@ static void free_path_list(path_list_node_t *node) * pointer on success * NULL on allocation problems */ -static path_list_node_t* add_path_list_node(path_list_node_t *node, REAL x, REAL y, BOOL type) +static path_list_node_t* add_path_list_node(path_list_node_t **node, REAL x, REAL y, BOOL type) { path_list_node_t *new; + if (!*node) + { + *node = heap_alloc_zero(sizeof(path_list_node_t)); + + if (!*node) + return NULL; + + (*node)->pt.X = x; + (*node)->pt.Y = y; + (*node)->type = type; + (*node)->next = NULL; + + return *node; + } + new = heap_alloc_zero(sizeof(path_list_node_t)); if(!new) return NULL; @@ -86,8 +101,8 @@ static path_list_node_t* add_path_list_node(path_list_node_t *node, REAL x, REAL new->pt.X = x; new->pt.Y = y; new->type = type; - new->next = node->next; - node->next = new; + new->next = (*node)->next; + (*node)->next = new; return new; } @@ -169,7 +184,7 @@ static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, R } else /* add a middle point */ - if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine))) + if(!(node = add_path_list_node(&start, mp[2].X, mp[2].Y, PathPointTypeLine))) return FALSE; /* do the same with halves */ @@ -1333,7 +1348,7 @@ GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatnes /* always add line points and start points */ if((type == PathPointTypeStart) || (type == PathPointTypeLine)){ - if(!add_path_list_node(node, pt.X, pt.Y, path->pathdata.Types[i])) + if(!add_path_list_node(&node, pt.X, pt.Y, path->pathdata.Types[i])) goto memout; node = node->next; @@ -1357,7 +1372,7 @@ GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatnes start = node; /* add Bezier end point */ type = (path->pathdata.Types[i] & ~PathPointTypePathTypeMask) | PathPointTypeLine; - if(!add_path_list_node(node, pt.X, pt.Y, type)) + if(!add_path_list_node(&node, pt.X, pt.Y, type)) goto memout; node = node->next; @@ -1829,7 +1844,7 @@ static void add_bevel_point(const GpPointF *endpoint, const GpPointF *nextpoint, if (segment_length == 0.0) { - *last_point = add_path_list_node(*last_point, endpoint->X, + *last_point = add_path_list_node(last_point, endpoint->X, endpoint->Y, PathPointTypeLine); return; } @@ -1845,7 +1860,7 @@ static void add_bevel_point(const GpPointF *endpoint, const GpPointF *nextpoint, bevel_dy = -distance * segment_dx / segment_length; } - *last_point = add_path_list_node(*last_point, endpoint->X + bevel_dx, + *last_point = add_path_list_node(last_point, endpoint->X + bevel_dx, endpoint->Y + bevel_dy, PathPointTypeLine); } @@ -1870,7 +1885,7 @@ static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF * float dy = (dy0*dy1*(dy0-dy1) + dx0*dx0*dy1 - dx1*dx1*dy0)/det; if (dx*dx + dy*dy < pen->miterlimit*pen->miterlimit * distance*distance) { - *last_point = add_path_list_node(*last_point, p2->X + dx, + *last_point = add_path_list_node(last_point, p2->X + dx, p2->Y + dy, PathPointTypeLine); break; } @@ -1933,11 +1948,11 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, } if (add_first_points) - *last_point = add_path_list_node(*last_point, endpoint->X - extend_dx + bevel_dx, + *last_point = add_path_list_node(last_point, endpoint->X - extend_dx + bevel_dx, endpoint->Y - extend_dy + bevel_dy, PathPointTypeLine); if (add_last_point) - *last_point = add_path_list_node(*last_point, endpoint->X - extend_dx - bevel_dx, + *last_point = add_path_list_node(last_point, endpoint->X - extend_dx - bevel_dx, endpoint->Y - extend_dy - bevel_dy, PathPointTypeLine); break; } @@ -1959,27 +1974,27 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, dy2 = dy * control_point_distance; /* first 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X + dy, + *last_point = add_path_list_node(last_point, endpoint->X + dy, endpoint->Y - dx, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2, + *last_point = add_path_list_node(last_point, endpoint->X + dy + dx2, endpoint->Y - dx + dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2, + *last_point = add_path_list_node(last_point, endpoint->X + dx + dy2, endpoint->Y + dy - dx2, PathPointTypeBezier); /* midpoint */ - *last_point = add_path_list_node(*last_point, endpoint->X + dx, + *last_point = add_path_list_node(last_point, endpoint->X + dx, endpoint->Y + dy, PathPointTypeBezier); /* second 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2, + *last_point = add_path_list_node(last_point, endpoint->X + dx - dy2, endpoint->Y + dy + dx2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2, + *last_point = add_path_list_node(last_point, endpoint->X - dy + dx2, endpoint->Y + dx + dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dy, + *last_point = add_path_list_node(last_point, endpoint->X - dy, endpoint->Y + dx, PathPointTypeBezier); } else if (add_last_point) @@ -2000,7 +2015,7 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, if (add_first_points) { add_bevel_point(endpoint, nextpoint, pen_width, 1, last_point); - *last_point = add_path_list_node(*last_point, endpoint->X - dx, + *last_point = add_path_list_node(last_point, endpoint->X - dx, endpoint->Y - dy, PathPointTypeLine); } if (add_first_points || add_last_point) @@ -2041,13 +2056,13 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, perp_dx = -distance * segment_dy / segment_length; perp_dy = distance * segment_dx / segment_length; - *last_point = add_path_list_node(*last_point, endpoint->X - par_dx - perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X - par_dx - perp_dx, endpoint->Y - par_dy - perp_dy, PathPointTypeStart); - *last_point = add_path_list_node(*last_point, endpoint->X - par_dx + perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X - par_dx + perp_dx, endpoint->Y - par_dy + perp_dy, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X + par_dx + perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X + par_dx + perp_dx, endpoint->Y + par_dy + perp_dy, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X + par_dx - perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X + par_dx - perp_dx, endpoint->Y + par_dy - perp_dy, PathPointTypeLine); break; } @@ -2066,39 +2081,39 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, dy2 = dy * control_point_distance; /* starting point */ - *last_point = add_path_list_node(*last_point, endpoint->X + dy, + *last_point = add_path_list_node(last_point, endpoint->X + dy, endpoint->Y - dx, PathPointTypeStart); /* first 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2, + *last_point = add_path_list_node(last_point, endpoint->X + dy + dx2, endpoint->Y - dx + dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2, + *last_point = add_path_list_node(last_point, endpoint->X + dx + dy2, endpoint->Y + dy - dx2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X + dx, + *last_point = add_path_list_node(last_point, endpoint->X + dx, endpoint->Y + dy, PathPointTypeBezier); /* second 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2, + *last_point = add_path_list_node(last_point, endpoint->X + dx - dy2, endpoint->Y + dy + dx2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2, + *last_point = add_path_list_node(last_point, endpoint->X - dy + dx2, endpoint->Y + dx + dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dy, + *last_point = add_path_list_node(last_point, endpoint->X - dy, endpoint->Y + dx, PathPointTypeBezier); /* third 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X - dy - dx2, + *last_point = add_path_list_node(last_point, endpoint->X - dy - dx2, endpoint->Y + dx - dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dx - dy2, + *last_point = add_path_list_node(last_point, endpoint->X - dx - dy2, endpoint->Y - dy + dx2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X - dx, + *last_point = add_path_list_node(last_point, endpoint->X - dx, endpoint->Y - dy, PathPointTypeBezier); /* fourth 90-degree arc */ - *last_point = add_path_list_node(*last_point, endpoint->X - dx + dy2, + *last_point = add_path_list_node(last_point, endpoint->X - dx + dy2, endpoint->Y - dy - dx2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X + dy - dx2, + *last_point = add_path_list_node(last_point, endpoint->X + dy - dx2, endpoint->Y - dx - dy2, PathPointTypeBezier); - *last_point = add_path_list_node(*last_point, endpoint->X + dy, + *last_point = add_path_list_node(last_point, endpoint->X + dy, endpoint->Y - dx, PathPointTypeBezier); break; @@ -2117,13 +2132,13 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, perp_dx = -pen_width * segment_dy / segment_length; perp_dy = pen_width * segment_dx / segment_length; - *last_point = add_path_list_node(*last_point, endpoint->X + par_dx, + *last_point = add_path_list_node(last_point, endpoint->X + par_dx, endpoint->Y + par_dy, PathPointTypeStart); - *last_point = add_path_list_node(*last_point, endpoint->X - perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X - perp_dx, endpoint->Y - perp_dy, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X - par_dx, + *last_point = add_path_list_node(last_point, endpoint->X - par_dx, endpoint->Y - par_dy, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X + perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X + perp_dx, endpoint->Y + perp_dy, PathPointTypeLine); break; } @@ -2137,11 +2152,11 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, REAL perp_dx = -par_dy; REAL perp_dy = par_dx; - *last_point = add_path_list_node(*last_point, endpoint->X, + *last_point = add_path_list_node(last_point, endpoint->X, endpoint->Y, PathPointTypeStart); - *last_point = add_path_list_node(*last_point, endpoint->X + SQRT3 * par_dx - perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X + SQRT3 * par_dx - perp_dx, endpoint->Y + SQRT3 * par_dy - perp_dy, PathPointTypeLine); - *last_point = add_path_list_node(*last_point, endpoint->X + SQRT3 * par_dx + perp_dx, + *last_point = add_path_list_node(last_point, endpoint->X + SQRT3 * par_dx + perp_dx, endpoint->Y + SQRT3 * par_dy + perp_dy, PathPointTypeLine); break; } @@ -2200,7 +2215,7 @@ static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint, /* rotation of CustomCap according to line */ perp_dx = custom->pathdata.Points[i].X * cosa + (custom->pathdata.Points[i].Y - 1.0) * sina; perp_dy = custom->pathdata.Points[i].X * sina - (custom->pathdata.Points[i].Y - 1.0) * cosa; - *last_point = add_path_list_node(*last_point, posx + perp_dx, + *last_point = add_path_list_node(last_point, posx + perp_dx, posy + perp_dy, custom->pathdata.Types[i]); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> This is required so as to be able to use it in the graphicspath module Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/gdiplus_private.h | 1 + dlls/gdiplus/graphics.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index c292318b06e..ad1b08f1517 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -67,6 +67,7 @@ extern GpStatus gdi_transform_acquire(GpGraphics *graphics); extern GpStatus gdi_transform_release(GpGraphics *graphics); extern GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space, GpCoordinateSpace src_space, GpMatrix *matrix); +extern GpStatus get_graphics_device_bounds(GpGraphics *, GpRectF *); extern GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space, GpCoordinateSpace src_space, GpPointF *points, INT count); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 6dc34707bbf..54c07a4db8c 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -2153,7 +2153,7 @@ static GpStatus restore_container(GpGraphics* graphics, return Ok; } -static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect) +GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect) { RECT wnd_rect; GpStatus stat=Ok; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> This should reduce the load on GDI when an application is trying to draw to regions outside of selected DC's region Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41617 Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/gdiplus_private.h | 1 + dlls/gdiplus/graphicspath.c | 158 +++++++++++++++++++++++++++++++++ dlls/gdiplus/region.c | 40 +++++++-- 3 files changed, 190 insertions(+), 9 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index ad1b08f1517..479723fc263 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -136,6 +136,7 @@ extern DWORD write_region_data(const GpRegion *region, void *data); extern DWORD write_path_data(GpPath *path, void *data); extern GpStatus trace_path(GpGraphics *graphics, GpPath *path); +extern GpStatus clip_path(GpGraphics *graphics, GpPath *in, GpPath *out); typedef struct region_element region_element; extern void delete_element(region_element *element); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 4287845fa9b..9fb3f97c978 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -2798,3 +2798,161 @@ DWORD write_path_data(GpPath *path, void *data) memset(types + i, 0, ((path->pathdata.Count + 3) & ~3) - path->pathdata.Count); return size; } + +GpStatus clip_path(GpGraphics *graphics, GpPath *in, GpPath *out) +{ + RectF rect; + int i; + path_list_node_t *list, *pos = NULL; + + get_graphics_device_bounds(graphics, &rect); + + for (i = 1; i < in->pathdata.Count - 1; i++) + { + RectF bounds; + + switch(in->pathdata.Types[i] & PathPointTypePathTypeMask) + { + case PathPointTypeBezier: + { + int j, leftmost = in->pathdata.Points[i - 1].X, rightmost = in->pathdata.Points[i - 1].X, + top = in->pathdata.Points[i - 1].Y, bottom = in->pathdata.Points[i - 1].Y; + + for (j = 0; j < 3; j++) + { + if (in->pathdata.Points[i + j].X < leftmost) + leftmost = in->pathdata.Points[i + j].X; + + if (in->pathdata.Points[i + j].X > rightmost) + rightmost = in->pathdata.Points[i + j].X; + + if (in->pathdata.Points[i + j].Y < top) + top = in->pathdata.Points[i + j].Y; + + if (in->pathdata.Points[i + j].Y > bottom) + bottom = in->pathdata.Points[i + j].Y; + } + + set_rect(&bounds, leftmost, top, rightmost - leftmost, bottom - top); + + /* properties of intersect rectangle */ + leftmost = (rect.X < bounds.X) ? bounds.X : rect.X; + top = (rect.Y < bounds.Y) ? bounds.Y : rect.Y; + + rightmost = (rect.X + rect.Width < bounds.X + bounds.Width) + ? rect.X + rect.Width : bounds.X + bounds.Width; + bottom = (rect.Y + rect.Height < bounds.Y + bounds.Height) + ? rect.Y + rect.Height : bounds.Y + bounds.Height; + + if (leftmost < rightmost && top < bottom) + { + /* restore the immediate previous point, if skipped, as it is needed to connect to the rest of the points */ + if (pos && pos->pt.X != in->pathdata.Points[i - 1].X && pos->pt.Y != in->pathdata.Points[i - 1].Y) + add_path_list_node(&list, in->pathdata.Points[i - 1].X, in->pathdata.Points[i - 1].Y, + PathPointTypeLine); + for(j = 0; j < 3; j++) + pos = add_path_list_node(&list, in->pathdata.Points[i + j].X, in->pathdata.Points[i + j].Y, + PathPointTypeBezier); + } + + i += 2; + break; + } + case PathPointTypeLine: + { + GpPointF tmp[4]; + + int j, leftmost = in->pathdata.Points[i - 1].X, rightmost = in->pathdata.Points[i - 1].X, + top = in->pathdata.Points[i - 1].Y, bottom = in->pathdata.Points[i - 1].Y; + + /* widen the path by a width of 10 */ + if (in->pathdata.Points[i - 1].Y != in->pathdata.Points[i].Y) + { + tmp[0].X = in->pathdata.Points[i - 1].X + 5; + tmp[0].Y = in->pathdata.Points[i - 1].Y; + tmp[1].X = in->pathdata.Points[i - 1].X - 5; + tmp[1].Y = in->pathdata.Points[i - 1].Y; + + tmp[2].X = in->pathdata.Points[i].X + 5; + tmp[2].Y = in->pathdata.Points[i].Y; + tmp[3].X = in->pathdata.Points[i].X - 5; + tmp[3].Y = in->pathdata.Points[i].Y; + } + else + { + tmp[0].X = in->pathdata.Points[i - 1].X; + tmp[0].Y = in->pathdata.Points[i - 1].Y + 5; + tmp[1].X = in->pathdata.Points[i - 1].X; + tmp[1].Y = in->pathdata.Points[i - 1].Y - 5; + + tmp[2].X = in->pathdata.Points[i].X; + tmp[2].Y = in->pathdata.Points[i].Y + 5; + tmp[3].X = in->pathdata.Points[i].X; + tmp[3].Y = in->pathdata.Points[i].Y - 5; + } + + for (j = 0; j < 4; j++) + { + if (tmp[j].X < leftmost) + leftmost = tmp[j].X; + + if (tmp[j].X > rightmost) + rightmost = tmp[j].X; + + if (tmp[j].Y < top) + top = tmp[j].Y; + + if (tmp[j].Y > bottom) + bottom = tmp[j].Y; + } + + set_rect(&bounds, leftmost, top, rightmost - leftmost, bottom - top); + + leftmost = (rect.X < bounds.X) ? bounds.X : rect.X; + top = (rect.Y < bounds.Y) ? bounds.Y : rect.Y; + + rightmost = (rect.X + rect.Width < bounds.X + bounds.Width) + ? rect.X + rect.Width : bounds.X + bounds.Width; + bottom = (rect.Y + rect.Height < bounds.Y + bounds.Height) + ? rect.Y + rect.Height : bounds.Y + bounds.Height; + + if (leftmost < rightmost && top < bottom) + { + if (pos && pos->pt.X != in->pathdata.Points[i - 1].X && pos->pt.Y != in->pathdata.Points[i - 1].Y) + add_path_list_node(&list, in->pathdata.Points[i - 1].X, in->pathdata.Points[i - 1].Y, + PathPointTypeLine); + + pos = add_path_list_node(&list, in->pathdata.Points[i].X, in->pathdata.Points[i].Y, + PathPointTypeLine); + } + } + case PathPointTypeStart: + add_path_list_node(&list, in->pathdata.Points[i].X, in->pathdata.Points[i].Y, + PathPointTypeBezier); + break; + + default: + ERR("Bad point type \n"); + } + } + + i = path_list_count(list); + + if (!lengthen_path(out, i)) + { + free_path_list(list); + return OutOfMemory; + } + + out->pathdata.Count = i; + + pos = list; + for (i = 0; i < out->pathdata.Count; i++){ + out->pathdata.Points[i] = pos->pt; + out->pathdata.Types[i] = pos->type; + pos = pos->next; + } + + free_path_list(list); + return Ok; +} diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index d0d9f4ba54e..ea5e3b08ed0 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -987,12 +987,13 @@ GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed) return Ok; } -static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) +static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, BOOL clip, HRGN *hrgn) { HDC new_hdc=NULL; GpGraphics *new_graphics=NULL; GpStatus stat; INT save_state; + GpPath *clippedpath; if (!path->pathdata.Count) /* PathToRegion doesn't support empty paths */ { @@ -1029,7 +1030,28 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) gdi_transform_acquire(graphics); - stat = trace_path(graphics, path); + if (clip) + { + GpFillMode fillmode; + + GdipGetPathFillMode(path, &fillmode); + stat = GdipCreatePath(fillmode, &clippedpath); + if (stat != Ok) + return stat; + + stat = clip_path(graphics, path, clippedpath); + + if (stat != Ok) + { + GdipDeletePath(clippedpath); + return stat; + } + + stat = trace_path(graphics, clippedpath); + } + else + stat = trace_path(graphics, path); + if (stat == Ok) { *hrgn = PathToRegion(graphics->hdc); @@ -1051,7 +1073,7 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) return stat; } -static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, HRGN *hrgn) +static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, BOOL clip, HRGN *hrgn) { switch (element->type) { @@ -1062,7 +1084,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap *hrgn = CreateRectRgn(0, 0, 0, 0); return *hrgn ? Ok : OutOfMemory; case RegionDataPath: - return get_path_hrgn(element->elementdata.path, graphics, hrgn); + return get_path_hrgn(element->elementdata.path, graphics, clip, hrgn); case RegionDataRect: { GpPath* path; @@ -1075,7 +1097,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap stat = GdipAddPathRectangle(path, rc->X, rc->Y, rc->Width, rc->Height); if (stat == Ok) - stat = get_path_hrgn(path, graphics, hrgn); + stat = get_path_hrgn(path, graphics, clip, hrgn); GdipDeletePath(path); @@ -1091,7 +1113,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap GpStatus stat; int ret; - stat = get_region_hrgn(element->elementdata.combine.left, graphics, &left); + stat = get_region_hrgn(element->elementdata.combine.left, graphics, clip, &left); if (stat != Ok) { *hrgn = NULL; @@ -1104,7 +1126,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap switch (element->type) { case CombineModeIntersect: - return get_region_hrgn(element->elementdata.combine.right, graphics, hrgn); + return get_region_hrgn(element->elementdata.combine.right, graphics, clip, hrgn); case CombineModeXor: case CombineModeExclude: left = CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22); break; @@ -1114,7 +1136,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap } } - stat = get_region_hrgn(element->elementdata.combine.right, graphics, &right); + stat = get_region_hrgn(element->elementdata.combine.right, graphics, clip, &right); if (stat != Ok) { DeleteObject(left); @@ -1190,7 +1212,7 @@ GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HR if (!region || !hrgn) return InvalidParameter; - return get_region_hrgn(®ion->node, graphics, hrgn); + return get_region_hrgn(®ion->node, graphics, FALSE, hrgn); } GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> Now that we added a flag to get_region_hrgn, we need to use get_region_hrgn directly instead of through GdipGetRegionHRgn inorder to access this flag Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/gdiplus_private.h | 1 + dlls/gdiplus/region.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 479723fc263..263c4ae1976 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -140,6 +140,7 @@ extern GpStatus clip_path(GpGraphics *graphics, GpPath *in, GpPath *out); typedef struct region_element region_element; extern void delete_element(region_element *element); +extern GpStatus get_region_hrgn(region_element *element, GpGraphics *graphics, BOOL clip, HRGN *hrgn); extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result); diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index ea5e3b08ed0..88f90c0be77 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -1073,7 +1073,7 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, BOOL clip, HRG return stat; } -static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, BOOL clip, HRGN *hrgn) +GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, BOOL clip, HRGN *hrgn) { switch (element->type) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> trace_path calls poly_draw which will in turn call SOFTWARE_GdipFillPath which will again call trace_path leading to a circular dependency Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/graphics.c | 47 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 54c07a4db8c..3845ce376ca 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -2050,13 +2050,52 @@ end: GpStatus trace_path(GpGraphics *graphics, GpPath *path) { - GpStatus result; + POINT *pti = heap_alloc_zero(path->pathdata.Count * sizeof(POINT)); + BYTE *tp = heap_alloc_zero(path->pathdata.Count); + GpPointF *ptcopy = heap_alloc_zero(path->pathdata.Count * sizeof(GpPointF)); + INT i; + GpStatus status = GenericError; + + if(!path->pathdata.Count){ + status = Ok; + goto end; + } + if(!pti || !tp || !ptcopy){ + status = OutOfMemory; + goto end; + } + + for(i = 1; i < path->pathdata.Count; i++){ + if((path->pathdata.Types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){ + if((i + 2 >= path->pathdata.Count) || !(path->pathdata.Types[i + 1] & PathPointTypeBezier) + || !(path->pathdata.Types[i + 2] & PathPointTypeBezier)){ + ERR("Bad bezier points\n"); + goto end; + } + i += 2; + } + } + + memcpy(ptcopy, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF)); + gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, path->pathdata.Count); + round_points(pti, ptcopy, path->pathdata.Count); + + for(i = 0; i < path->pathdata.Count; i++){ + tp[i] = convert_path_point_type(path->pathdata.Types[i]); + } BeginPath(graphics->hdc); - result = draw_poly(graphics, NULL, path->pathdata.Points, - path->pathdata.Types, path->pathdata.Count, FALSE); + PolyDraw(graphics->hdc, pti, tp, path->pathdata.Count); EndPath(graphics->hdc); - return result; + + status = Ok; + +end: + heap_free(pti); + heap_free(ptcopy); + heap_free(tp); + + return status; } typedef enum GraphicsContainerType { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
From: David Kahurani <k.kahurani(a)gmail.com> Signed-off-by: David Kahurani <k.kahurani(a)gmail.com> --- dlls/gdiplus/graphics.c | 103 ++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 3845ce376ca..5e027e29631 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -50,6 +50,7 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix); +static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path); /* Converts from gdiplus path point type to gdi path point type. */ static BYTE convert_path_point_type(BYTE type) { @@ -1921,28 +1922,28 @@ static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev) } /* Draws a combination of bezier curves and lines between points. */ -static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt, - GDIPCONST BYTE * types, INT count, BOOL caps) +static GpStatus draw_path(GpGraphics *graphics, GpPen *pen, GpPath *path, BOOL caps) { - POINT *pti = heap_alloc_zero(count * sizeof(POINT)); - BYTE *tp = heap_alloc_zero(count); - GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF)); - INT i, j; + GpPath *clonedpath; + INT i, count = path->pathdata.Count; GpStatus status = GenericError; - if(!count){ + if(!path->pathdata.Count){ status = Ok; goto end; } - if(!pti || !tp || !ptcopy){ + + GdipClonePath(path, &clonedpath); + + if(!clonedpath){ status = OutOfMemory; goto end; } - for(i = 1; i < count; i++){ - if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){ - if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier) - || !(types[i + 2] & PathPointTypeBezier)){ + for(i = 1; i < clonedpath->pathdata.Count; i++){ + if((clonedpath->pathdata.Types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){ + if((i + 2 >= count) || !(clonedpath->pathdata.Types[i + 1] & PathPointTypeBezier) + || !(clonedpath->pathdata.Types[i + 2] & PathPointTypeBezier)){ ERR("Bad bezier points\n"); goto end; } @@ -1950,38 +1951,36 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * } } - memcpy(ptcopy, pt, count * sizeof(GpPointF)); - /* If we are drawing caps, go through the points and adjust them accordingly, * and draw the caps. */ if(caps){ - switch(types[count - 1] & PathPointTypePathTypeMask){ + switch(clonedpath->pathdata.Types[count - 1] & PathPointTypePathTypeMask){ case PathPointTypeBezier: if(pen->endcap == LineCapArrowAnchor) - shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE); + shorten_bezier_amt(&clonedpath->pathdata.Points[count - 4], pen->width, FALSE); else if((pen->endcap == LineCapCustom) && pen->customend) - shorten_bezier_amt(&ptcopy[count - 4], + shorten_bezier_amt(&clonedpath->pathdata.Points[count - 4], pen->width * pen->customend->inset, FALSE); draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend, - pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X), - pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y), - pt[count - 1].X, pt[count - 1].Y); + path->pathdata.Points[count - 1].X - (clonedpath->pathdata.Points[count - 1].X - clonedpath->pathdata.Points[count - 2].X), + path->pathdata.Points[count - 1].Y - (clonedpath->pathdata.Points[count - 1].Y - clonedpath->pathdata.Points[count - 2].Y), + path->pathdata.Points[count - 1].X, path->pathdata.Points[count - 1].Y); break; case PathPointTypeLine: if(pen->endcap == LineCapArrowAnchor) - shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y, - &ptcopy[count - 1].X, &ptcopy[count - 1].Y, + shorten_line_amt(clonedpath->pathdata.Points[count - 2].X, clonedpath->pathdata.Points[count - 2].Y, + &clonedpath->pathdata.Points[count - 1].X, &clonedpath->pathdata.Points[count - 1].Y, pen->width); else if((pen->endcap == LineCapCustom) && pen->customend) - shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y, - &ptcopy[count - 1].X, &ptcopy[count - 1].Y, + shorten_line_amt(clonedpath->pathdata.Points[count - 2].X, clonedpath->pathdata.Points[count - 2].Y, + &clonedpath->pathdata.Points[count - 1].X, &clonedpath->pathdata.Points[count - 1].Y, pen->customend->inset * pen->width); draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend, - pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, - pt[count - 1].Y); + path->pathdata.Points[count - 2].X, path->pathdata.Points[count - 2].Y, path->pathdata.Points[count - 1].X, + path->pathdata.Points[count - 1].Y); break; default: @@ -1990,36 +1989,36 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * } /* Find start of points */ - for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask) - == PathPointTypeStart); j++); + for(i = 1; i < count && ((clonedpath->pathdata.Types[i] & PathPointTypePathTypeMask) + == PathPointTypeStart); i++); - switch(types[j] & PathPointTypePathTypeMask){ + switch(clonedpath->pathdata.Types[i] & PathPointTypePathTypeMask){ case PathPointTypeBezier: if(pen->startcap == LineCapArrowAnchor) - shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE); + shorten_bezier_amt(&clonedpath->pathdata.Points[i - 1], pen->width, TRUE); else if((pen->startcap == LineCapCustom) && pen->customstart) - shorten_bezier_amt(&ptcopy[j - 1], + shorten_bezier_amt(&clonedpath->pathdata.Points[i - 1], pen->width * pen->customstart->inset, TRUE); draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart, - pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X), - pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y), - pt[j - 1].X, pt[j - 1].Y); + path->pathdata.Points[i - 1].X - (clonedpath->pathdata.Points[i - 1].X - clonedpath->pathdata.Points[i].X), + path->pathdata.Points[i - 1].Y - (clonedpath->pathdata.Points[i - 1].Y - clonedpath->pathdata.Points[i].Y), + path->pathdata.Points[i - 1].X, path->pathdata.Points[i - 1].Y); break; case PathPointTypeLine: if(pen->startcap == LineCapArrowAnchor) - shorten_line_amt(ptcopy[j].X, ptcopy[j].Y, - &ptcopy[j - 1].X, &ptcopy[j - 1].Y, + shorten_line_amt(clonedpath->pathdata.Points[i].X, clonedpath->pathdata.Points[i].Y, + &clonedpath->pathdata.Points[i - 1].X, &clonedpath->pathdata.Points[i - 1].Y, pen->width); else if((pen->startcap == LineCapCustom) && pen->customstart) - shorten_line_amt(ptcopy[j].X, ptcopy[j].Y, - &ptcopy[j - 1].X, &ptcopy[j - 1].Y, + shorten_line_amt(clonedpath->pathdata.Points[i].X, clonedpath->pathdata.Points[i].Y, + &clonedpath->pathdata.Points[i - 1].X, &clonedpath->pathdata.Points[i - 1].Y, pen->customstart->inset * pen->width); draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart, - pt[j].X, pt[j].Y, pt[j - 1].X, - pt[j - 1].Y); + path->pathdata.Points[i].X, path->pathdata.Points[i].Y, path->pathdata.Points[i - 1].X, + path->pathdata.Points[i - 1].Y); break; default: @@ -2028,23 +2027,13 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * } } - gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count); - - round_points(pti, ptcopy, count); - - for(i = 0; i < count; i++){ - tp[i] = convert_path_point_type(types[i]); - } - - PolyDraw(graphics->hdc, pti, tp, count); + GdipCreatePath(FillModeAlternate, &path); + SOFTWARE_GdipFillPath(graphics, pen->brush, clonedpath); status = Ok; end: - heap_free(pti); - heap_free(ptcopy); - heap_free(tp); - + GdipDeletePath(clonedpath); return status; } @@ -3659,8 +3648,7 @@ static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *pat gdi_transform_acquire(graphics); - retval = draw_poly(graphics, pen, path->pathdata.Points, - path->pathdata.Types, path->pathdata.Count, TRUE); + retval = draw_path(graphics, pen, path, TRUE); gdi_transform_release(graphics); @@ -4340,8 +4328,7 @@ static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath gdi_transform_acquire(graphics); BeginPath(graphics->hdc); - retval = draw_poly(graphics, NULL, path->pathdata.Points, - path->pathdata.Types, path->pathdata.Count, FALSE); + retval = draw_path(graphics, NULL, path, FALSE); if(retval == Ok) { @@ -4634,7 +4621,7 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush, ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); DeleteObject(hrgn); - status = GdipGetRegionHRgn(region, graphics, &hrgn); + status = get_region_hrgn(®ion->node, graphics, FALSE, &hrgn); if (status != Ok) { RestoreDC(graphics->hdc, save_state); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3288
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=141310 Your paranoid android. === debian11 (build log) === error: patch failed: dlls/gdiplus/graphicspath.c:75 error: patch failed: dlls/gdiplus/gdiplus_private.h:67 error: patch failed: dlls/gdiplus/graphics.c:1921 Task: Patch failed to apply === debian11b (build log) === error: patch failed: dlls/gdiplus/graphicspath.c:75 error: patch failed: dlls/gdiplus/gdiplus_private.h:67 error: patch failed: dlls/gdiplus/graphics.c:1921 Task: Patch failed to apply
participants (3)
-
David Kahurani -
David Kahurani (@kahurani) -
Marvin