I wrote a test intending to find exactly how points are quantized when figuring the region bounding box, so I can make sure I don't break it. Surprisingly, I'm able to just pass in fractional values and get them back. Clearly this isn't based on any quantization.
MSDN says "The rectangle returned by Region::GetBounds is not always the smallest possible rectangle."
MSDN also says that the world and page transformations of the Graphics object are used, which doesn't seem to be the case, but I choose to selectively believe it when tests agree.
So the rectangle we return must enclose the region, but it may be larger than necessary to enclose the region. This is conveniently exactly the same as the get_region_bounding_box function I wrote earlier. (While there may be implementation differences, I think we can worry about that if/when it becomes a problem. We already got by on something very different from native for a long time.)
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/tests/region.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c index 1bcef8a35dd..375f82f9ea7 100644 --- a/dlls/gdiplus/tests/region.c +++ b/dlls/gdiplus/tests/region.c @@ -1732,6 +1732,7 @@ static void test_scans(void) static void test_getbounds(void) { GpRegion *region; + GpPath *path; GpGraphics *graphics; GpStatus status; GpRectF rectf; @@ -1807,6 +1808,44 @@ static void test_getbounds(void) ok(rectf.Width == 100.0, "Expected width = 0.0, got %.2f\n", rectf.Width); ok(rectf.Height == 100.0, "Expected height = 0.0, got %.2f\n", rectf.Height);
+ /* coordinates are not rounded */ + status = GdipResetWorldTransform(graphics); + ok(status == Ok, "status %08x\n", status); + status = GdipResetPageTransform(graphics); + ok(status == Ok, "status %08x\n", status); + rectf.X = 0.125; + rectf.Y = 1.125; + rectf.Width = 2.125; + rectf.Height = 3.125; + status = GdipCombineRegionRect(region, &rectf, CombineModeReplace); + ok(status == Ok, "status %08x\n", status); + rectf.X = rectf.Y = 0.0; + rectf.Height = rectf.Width = 0.0; + status = GdipGetRegionBounds(region, graphics, &rectf); + ok(status == Ok, "status %08x\n", status); + todo_wine ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + todo_wine ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + todo_wine ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + todo_wine ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + + /* test path */ + status = GdipCreatePath(FillModeAlternate, &path); + ok(status == Ok, "status %08x\n", status); + status = GdipAddPathRectangle(path, 0.125, 1.125, 2.125, 3.125); + ok(status == Ok, "status %08x\n", status); + status = GdipCombineRegionPath(region, path, CombineModeReplace); + ok(status == Ok, "status %08x\n", status); + status = GdipDeletePath(path); + ok(status == Ok, "status %08x\n", status); + rectf.X = rectf.Y = 0.0; + rectf.Height = rectf.Width = 0.0; + status = GdipGetRegionBounds(region, graphics, &rectf); + ok(status == Ok, "status %08x\n", status); + todo_wine ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + todo_wine ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + todo_wine ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + todo_wine ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + status = GdipDeleteRegion(region); ok(status == Ok, "status %08x\n", status); status = GdipDeleteGraphics(graphics);
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/region.c | 33 +++++++++++++++++---------------- dlls/gdiplus/tests/region.c | 16 ++++++++-------- 2 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index 3034dedae76..79b073f6df2 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -104,6 +104,9 @@ typedef struct packed_point short Y; } packed_point;
+static void get_region_bounding_box(struct region_element *element, + REAL *min_x, REAL *min_y, REAL *max_x, REAL *max_y, BOOL *empty, BOOL *infinite); + static inline INT get_element_size(const region_element* element) { INT needed = sizeof(DWORD); /* DWORD for the type */ @@ -579,8 +582,8 @@ GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region) */ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect) { - HRGN hrgn; - RECT r; + REAL min_x, min_y, max_x, max_y; + BOOL empty, infinite; GpStatus status;
TRACE("(%p, %p, %p)\n", region, graphics, rect); @@ -589,31 +592,29 @@ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, return InvalidParameter;
/* Contrary to MSDN, native ignores the graphics transform. */ - status = GdipGetRegionHRgn(region, NULL, &hrgn); - if(status != Ok) - return status; + get_region_bounding_box(®ion->node, &min_x, &min_y, &max_x, &max_y, &empty, &infinite);
/* infinite */ - if(!hrgn){ + if(infinite){ rect->X = rect->Y = -(REAL)(1 << 22); rect->Width = rect->Height = (REAL)(1 << 23); TRACE("%p => infinite\n", region); return Ok; }
- if(GetRgnBox(hrgn, &r)){ - rect->X = r.left; - rect->Y = r.top; - rect->Width = r.right - r.left; - rect->Height = r.bottom - r.top; - TRACE("%p => %s\n", region, debugstr_rectf(rect)); + if(empty){ + rect->X = rect->Y = rect->Width = rect->Height = 0.0; + TRACE("%p => empty\n", region); + return Ok; } - else - status = GenericError;
- DeleteObject(hrgn); + rect->X = min_x; + rect->Y = min_y; + rect->Width = max_x - min_x; + rect->Height = max_y - min_y; + TRACE("%p => %s\n", region, debugstr_rectf(rect));
- return status; + return Ok; }
/***************************************************************************** diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c index 375f82f9ea7..93c6c53ebc1 100644 --- a/dlls/gdiplus/tests/region.c +++ b/dlls/gdiplus/tests/region.c @@ -1823,10 +1823,10 @@ static void test_getbounds(void) rectf.Height = rectf.Width = 0.0; status = GdipGetRegionBounds(region, graphics, &rectf); ok(status == Ok, "status %08x\n", status); - todo_wine ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); - todo_wine ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); - todo_wine ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); - todo_wine ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height);
/* test path */ status = GdipCreatePath(FillModeAlternate, &path); @@ -1841,10 +1841,10 @@ static void test_getbounds(void) rectf.Height = rectf.Width = 0.0; status = GdipGetRegionBounds(region, graphics, &rectf); ok(status == Ok, "status %08x\n", status); - todo_wine ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); - todo_wine ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); - todo_wine ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); - todo_wine ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height);
status = GdipDeleteRegion(region); ok(status == Ok, "status %08x\n", status);
I forgot to mention: my assumption is that GdipGetRegionBounds is supposed to sacrifice accuracy in favor of efficiency.
This merge request was approved by David Kahurani.