gdiplus: Implement GdipIsVisibleRect.
diff --git a/dlls/gdiplus/gdiplus.spec b/dlls/gdiplus/gdiplus.spec
index 2fc4686..bda241e 100644
--- a/dlls/gdiplus/gdiplus.spec
+++ b/dlls/gdiplus/gdiplus.spec
@@ -429,7 +429,7 @@
 @ stdcall GdipIsVisiblePathPointI(ptr long long ptr ptr)
 @ stdcall GdipIsVisiblePoint(ptr long long ptr)
 @ stdcall GdipIsVisiblePointI(ptr long long ptr)
-@ stub GdipIsVisibleRect
+@ stdcall GdipIsVisibleRect(ptr long long long long ptr)
 @ stdcall GdipIsVisibleRectI(ptr long long long long ptr)
 @ stdcall GdipIsVisibleRegionPoint(ptr long long ptr ptr)
 @ stdcall GdipIsVisibleRegionPointI(ptr long long ptr ptr)
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 3d35f74..5263910 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -3288,6 +3288,50 @@
     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
 }
 
+GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
+{
+    GpStatus stat;
+    GpRegion* rgn;
+    GpPointF pts[2];
+
+    TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
+
+    if(!graphics || !result)
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    pts[0].X = x;
+    pts[0].Y = y;
+    pts[1].X = x + width;
+    pts[1].Y = y + height;
+
+    if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
+                    CoordinateSpaceWorld, pts, 2)) != Ok)
+        return stat;
+
+    pts[1].X -= pts[0].X;
+    pts[1].Y -= pts[0].Y;
+
+    if((stat = GdipCreateRegion(&rgn)) != Ok)
+        return stat;
+
+    if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
+        goto cleanup;
+
+    stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
+
+cleanup:
+    GdipDeleteRegion(rgn);
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
+{
+    return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
+}
+
 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
@@ -4190,12 +4234,3 @@
     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
     return NotImplemented;
 }
-
-/*****************************************************************************
- * GdipIsVisibleRectI [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
-{
-    FIXME("(%p %d %d %d %d %p): stub\n", graphics, x, y, width, height, result);
-    return NotImplemented;
-}
diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c
index f74f7af..2a03aad 100644
--- a/dlls/gdiplus/tests/graphics.c
+++ b/dlls/gdiplus/tests/graphics.c
@@ -2107,6 +2107,168 @@
     ReleaseDC(0, hdc);
 }
 
+static void test_GdipIsVisibleRect(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    HDC hdc = GetDC(0);
+    REAL x, y, width, height;
+    BOOL val;
+
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipIsVisibleRect(NULL, 0, 0, 0, 0, &val);
+    expect(InvalidParameter, status);
+
+    status = GdipIsVisibleRect(graphics, 0, 0, 0, 0, NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipIsVisibleRectI(NULL, 0, 0, 0, 0, &val);
+    expect(InvalidParameter, status);
+
+    status = GdipIsVisibleRectI(graphics, 0, 0, 0, 0, NULL);
+    expect(InvalidParameter, status);
+
+    /* entirely within the visible region */
+    x = 0; width = 10;
+    y = 0; height = 10;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* partially outside */
+    x = -10; width = 20;
+    y = -10; height = 20;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* entirely outside */
+    x = -10; width = 5;
+    y = -10; height = 5;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    status = GdipSetClipRect(graphics, 10, 20, 30, 40, CombineModeReplace);
+    expect(Ok, status);
+
+    /* entirely within the visible region */
+    x = 12; width = 10;
+    y = 22; height = 10;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* partially outside */
+    x = 35; width = 10;
+    y = 55; height = 10;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* entirely outside */
+    x = 45; width = 5;
+    y = 65; height = 5;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    /* translate into center of clipping rect */
+    GdipTranslateWorldTransform(graphics, 25, 40, MatrixOrderAppend);
+
+    x = 0; width = 10;
+    y = 0; height = 10;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    x = 25; width = 5;
+    y = 40; height = 5;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    GdipTranslateWorldTransform(graphics, -25, -40, MatrixOrderAppend);
+
+    /* corners entirely outside, but some intersections */
+    x = 0; width = 70;
+    y = 0; height = 90;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    x = 0; width = 70;
+    y = 0; height = 30;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    x = 0; width = 30;
+    y = 0; height = 90;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* edge cases */
+    x = 0; width = 10;
+    y = 20; height = 40;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    x = 10; width = 30;
+    y = 0; height = 20;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    x = 40; width = 10;
+    y = 20; height = 40;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    x = 10; width = 30;
+    y = 60; height = 10;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == FALSE, "Expected (%.2f, %.2f, %.2f, %.2f) not to be visible\n", x, y, width, height);
+
+    /* rounding tests */
+    x = 0.4; width = 10.4;
+    y = 20; height = 40;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    x = 10; width = 30;
+    y = 0.4; height = 20.4;
+    status = GdipIsVisibleRect(graphics, x, y, width, height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    /* integer version */
+    x = 0; width = 30;
+    y = 0; height = 90;
+    status = GdipIsVisibleRectI(graphics, (INT)x, (INT)y, (INT)width, (INT)height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    x = 12; width = 10;
+    y = 22; height = 10;
+    status = GdipIsVisibleRectI(graphics, (INT)x, (INT)y, (INT)width, (INT)height, &val);
+    expect(Ok, status);
+    ok(val == TRUE, "Expected (%.2f, %.2f, %.2f, %.2f) to be visible\n", x, y, width, height);
+
+    GdipDeleteGraphics(graphics);
+    ReleaseDC(0, hdc);
+}
+
 START_TEST(graphics)
 {
     struct GdiplusStartupInput gdiplusStartupInput;
@@ -2135,6 +2297,7 @@
     test_GdipDrawString();
     test_GdipGetVisibleClipBounds();
     test_GdipIsVisiblePoint();
+    test_GdipIsVisibleRect();
     test_Get_Release_DC();
     test_BeginContainer2();
     test_transformpoints();