Rasterizing the full HRGN isn't really necessary when we only care about a single point.
For right now, we call GdipIsVisiblePathPoint, which does rasterize the full path, but that can easily be fixed later.
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/tests/region.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c index d6550b7d035..574cf87c656 100644 --- a/dlls/gdiplus/tests/region.c +++ b/dlls/gdiplus/tests/region.c @@ -2012,6 +2012,39 @@ static void test_isvisiblepoint(void) status = GdipTranslateWorldTransform(graphics, -25, -40, MatrixOrderAppend); expect(Ok, status);
+ /* fractional region */ + rectf.X = 4.25; + rectf.Y = 5.25; + rectf.Width = 6; + rectf.Height = 7; + + status = GdipCombineRegionRect(region, &rectf, CombineModeReplace); + expect(Ok, status); + + x = 4; + y = 5; + status = GdipIsVisibleRegionPoint(region, x, y, graphics, &res); + expect(Ok, status); + ok(res == FALSE, "Expected (%.2f, %.2f) to not be visible\n", x, y); + + x = 4.3; + y = 5.3; + status = GdipIsVisibleRegionPoint(region, x, y, graphics, &res); + expect(Ok, status); + ok(res == FALSE, "Expected (%.2f, %.2f) to not be visible\n", x, y); + + /* Point is rounded in device coordinates, so this gives more precision */ + status = GdipScaleWorldTransform(graphics, 20.0, 20.0, MatrixOrderAppend); + expect(Ok, status); + + status = GdipIsVisibleRegionPoint(region, x, y, graphics, &res); + expect(Ok, status); +todo_wine + ok(res == TRUE, "Expected (%.2f, %.2f) to be visible\n", x, y); + + status = GdipResetWorldTransform(graphics); + expect(Ok, status); + /* region from path */ status = GdipCreatePath(FillModeAlternate, &path); expect(Ok, status);
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/region.c | 47 +++++++++++++++++++++++++++++++++++++ dlls/gdiplus/tests/region.c | 1 - 2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index f2f33b5e7a5..a11a3594b10 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -1736,12 +1736,53 @@ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, G GpStatus stat; REAL min_x, min_y, max_x, max_y; BOOL empty, infinite; + GpMatrix transform; + BOOL identity; + GpRegion* tmp_region = NULL;
TRACE("(%p, %.2f, %.2f, %p, %p)\n", region, x, y, graphics, res);
if(!region || !res) return InvalidParameter;
+ if (graphics) + { + stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &transform); + if (stat != Ok) + return stat; + + stat = GdipIsMatrixIdentity(&transform, &identity); + if (stat != Ok) + return stat; + } + else + identity = TRUE; + + if (!identity) + { + GpPointF pt = {x, y}; + + stat = GdipTransformMatrixPoints(&transform, &pt, 1); + if (stat != Ok) + return stat; + + x = pt.X; + y = pt.Y; + + stat = GdipCloneRegion(region, &tmp_region); + if (stat != Ok) + return stat; + + stat = GdipTransformRegion(tmp_region, &transform); + if (stat != Ok) + { + GdipDeleteRegion(tmp_region); + return stat; + } + + region = tmp_region; + } + x = gdip_round(x); y = gdip_round(y);
@@ -1750,11 +1791,17 @@ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, G if (empty || x < min_x || y < min_y || x > max_x || y > max_y) { *res = infinite; + GdipDeleteRegion(tmp_region); return Ok; }
if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok) + { + GdipDeleteRegion(tmp_region); return stat; + } + + GdipDeleteRegion(tmp_region);
/* infinite */ if(!hrgn){ diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c index 574cf87c656..00837096092 100644 --- a/dlls/gdiplus/tests/region.c +++ b/dlls/gdiplus/tests/region.c @@ -2039,7 +2039,6 @@ static void test_isvisiblepoint(void)
status = GdipIsVisibleRegionPoint(region, x, y, graphics, &res); expect(Ok, status); -todo_wine ok(res == TRUE, "Expected (%.2f, %.2f) to be visible\n", x, y);
status = GdipResetWorldTransform(graphics);
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/region.c | 109 ++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 31 deletions(-)
diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index a11a3594b10..cb958082748 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -1727,15 +1727,89 @@ static void get_region_bounding_box(struct region_element *element, } }
+GpStatus point_in_region(struct region_element *element, REAL x, REAL y, BOOL *res) +{ + switch (element->type) + { + case RegionDataInfiniteRect: + *res = TRUE; + return Ok; + case RegionDataEmptyRect: + *res = FALSE; + return Ok; + case RegionDataPath: + return GdipIsVisiblePathPoint(element->elementdata.path, x, y, NULL, res); + case RegionDataRect: + { + REAL xadj = x + RGN_ROUND_OFS; + REAL yadj = y + RGN_ROUND_OFS; + GpRectF* rc = &element->elementdata.rect; + + *res = (xadj >= rc->X && yadj >= rc->Y && + xadj < rc->X + rc->Width && yadj < rc->Y + rc->Height); + + return Ok; + } + case CombineModeIntersect: + case CombineModeUnion: + case CombineModeXor: + case CombineModeExclude: + case CombineModeComplement: + { + BOOL left, right; + GpStatus stat; + + stat = point_in_region(element->elementdata.combine.left, x, y, &left); + + if (stat != Ok) + return stat; + + switch (element->type) + { + case CombineModeIntersect: + if (left) + return point_in_region(element->elementdata.combine.right, x, y, res); + *res = FALSE; + return Ok; + case CombineModeUnion: + if (!left) + return point_in_region(element->elementdata.combine.right, x, y, res); + *res = TRUE; + return Ok; + case CombineModeXor: + stat = point_in_region(element->elementdata.combine.right, x, y, &right); + if (stat == Ok) + *res = left ^ right; + return stat; + case CombineModeExclude: + if (left) + { + stat = point_in_region(element->elementdata.combine.right, x, y, &right); + if (stat == Ok) + *res = !right; + } + else + *res = FALSE; + return stat; + case CombineModeComplement: + if (!left) + return point_in_region(element->elementdata.combine.right, x, y, res); + *res = FALSE; + return Ok; + } + } + default: + FIXME("point_in_region unimplemented for region type=%lx\n", element->type); + return NotImplemented; + } +} + /***************************************************************************** * GdipIsVisibleRegionPoint [GDIPLUS.@] */ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, GpGraphics *graphics, BOOL *res) { - HRGN hrgn; GpStatus stat; - REAL min_x, min_y, max_x, max_y; - BOOL empty, infinite; GpMatrix transform; BOOL identity; GpRegion* tmp_region = NULL; @@ -1786,34 +1860,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, G x = gdip_round(x); y = gdip_round(y);
- /* Check for cases where we can skip quantization. */ - get_region_bounding_box(®ion->node, &min_x, &min_y, &max_x, &max_y, &empty, &infinite); - if (empty || x < min_x || y < min_y || x > max_x || y > max_y) - { - *res = infinite; - GdipDeleteRegion(tmp_region); - return Ok; - } - - if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok) - { - GdipDeleteRegion(tmp_region); - return stat; - } - - GdipDeleteRegion(tmp_region); - - /* infinite */ - if(!hrgn){ - *res = TRUE; - return Ok; - } - - *res = PtInRegion(hrgn, x, y); - - DeleteObject(hrgn); - - return Ok; + return point_in_region(®ion->node, x, y, res); }
/*****************************************************************************