From: David Kahurani k.kahurani@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@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)