[2/2] gdiplus: Store graphics clipping region in device coordinates.

Dmitry Timoshkov dmitry at baikal.ru
Thu Jul 18 21:34:42 CDT 2013


This patch fixes all the clipping region tests, vg_scene sample applications
(which were broken by previous revert), and a complex application I have here
that depends on correct clipping region behaviour on a graphics device with
various world transforms applied.
---
 dlls/gdiplus/gdiplus_private.h |  2 +-
 dlls/gdiplus/graphics.c        | 88 ++++++++++++++++++++++++++++++++++++++----
 dlls/gdiplus/tests/graphics.c  | 26 ++-----------
 3 files changed, 85 insertions(+), 31 deletions(-)

diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index f2214b5..00be810 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -166,7 +166,7 @@ struct GpGraphics{
     REAL xres, yres;
     GpMatrix worldtrans; /* world transform */
     BOOL busy;      /* hdc handle obtained by GdipGetDC */
-    GpRegion *clip;
+    GpRegion *clip; /* in device coords */
     UINT textcontrast; /* not used yet. get/set only */
     struct list containers;
     GraphicsContainer contid; /* last-issued container ID */
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index e0465ad..43464c9 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -51,6 +51,29 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
                                    GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
                                    INT flags, GDIPCONST GpMatrix *matrix);
 
+static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
+        GpCoordinateSpace src_space, GpMatrix *matrix);
+
+static void transform_rectf(GpGraphics *graphics, GpCoordinateSpace dst_space,
+        GpCoordinateSpace src_space, GpRectF *rect)
+{
+    GpPointF pt[3];
+
+    pt[0].X = rect->X;
+    pt[0].Y = rect->Y;
+    pt[1].X = rect->X + rect->Width;
+    pt[1].Y = rect->Y;
+    pt[2].X = rect->X;
+    pt[2].Y = rect->Y + rect->Height;
+    GdipTransformPoints(graphics, dst_space, src_space, pt, 3);
+    rect->X = pt[0].X;
+    rect->Y = pt[0].Y;
+    rect->Width = sqrt((pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y) +
+                     (pt[1].X - pt[0].X) * (pt[1].X - pt[0].X));
+    rect->Height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y) +
+                      (pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
+}
+
 /* Converts from gdiplus path point type to gdi path point type. */
 static BYTE convert_path_point_type(BYTE type)
 {
@@ -283,9 +306,6 @@ static void restore_dc(GpGraphics *graphics, INT state)
     RestoreDC(graphics->hdc, state);
 }
 
-static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
-        GpCoordinateSpace src_space, GpMatrix *matrix);
-
 /* This helper applies all the changes that the points listed in ptf need in
  * order to be drawn on the device context.  In the end, this should include at
  * least:
@@ -349,7 +369,8 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_
 
 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
 {
-    return GdipGetRegionHRgn(graphics->clip, graphics, hrgn);
+    /* clipping region is in device coords */
+    return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
 }
 
 /* Draw non-premultiplied ARGB data to the given graphics object */
@@ -4046,6 +4067,8 @@ GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
  */
 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
 {
+    GpStatus status;
+
     TRACE("(%p, %p)\n", graphics, rect);
 
     if(!graphics)
@@ -4054,7 +4077,11 @@ GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipGetRegionBounds(graphics->clip, graphics, rect);
+    status = GdipGetRegionBounds(graphics->clip, graphics, rect);
+    if (status == Ok)
+        transform_rectf(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, rect);
+
+    return status;
 }
 
 /*****************************************************************************
@@ -5397,11 +5424,15 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
     if(!graphics)
         return InvalidParameter;
 
+    if(graphics->busy)
+        return ObjectBusy;
+
+    /* hrgn is already in device units */
     status = GdipCreateRegionHrgn(hrgn, &region);
     if(status != Ok)
         return status;
 
-    status = GdipSetClipRegion(graphics, region, mode);
+    status = GdipCombineRegionRegion(graphics->clip, region, mode);
 
     GdipDeleteRegion(region);
     return status;
@@ -5409,6 +5440,9 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
 
 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
 {
+    GpStatus status;
+    GpPath *clip_path;
+
     TRACE("(%p, %p, %d)\n", graphics, path, mode);
 
     if(!graphics)
@@ -5417,7 +5451,20 @@ GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineM
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipCombineRegionPath(graphics->clip, path, mode);
+    status = GdipClonePath(path, &clip_path);
+    if (status == Ok)
+    {
+        GpMatrix world_to_device;
+
+        get_graphics_transform(graphics, CoordinateSpaceDevice,
+                               CoordinateSpaceWorld, &world_to_device);
+        status = GdipTransformPath(clip_path, &world_to_device);
+        if (status == Ok)
+            GdipCombineRegionPath(graphics->clip, clip_path, mode);
+
+        GdipDeletePath(clip_path);
+    }
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
@@ -5438,6 +5485,7 @@ GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
     rect.Y = y;
     rect.Width  = width;
     rect.Height = height;
+    transform_rectf(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &rect);
 
     return GdipCombineRegionRect(graphics->clip, &rect, mode);
 }
@@ -5460,6 +5508,9 @@ GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
                                       CombineMode mode)
 {
+    GpStatus status;
+    GpRegion *clip;
+
     TRACE("(%p, %p, %d)\n", graphics, region, mode);
 
     if(!graphics || !region)
@@ -5468,7 +5519,19 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipCombineRegionRegion(graphics->clip, region, mode);
+    status = GdipCloneRegion(region, &clip);
+    if (status == Ok)
+    {
+        GpMatrix world_to_device;
+
+        get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
+        status = GdipTransformRegion(clip, &world_to_device);
+        if (status == Ok)
+            status = GdipCombineRegionRegion(graphics->clip, clip, mode);
+
+        GdipDeleteRegion(clip);
+    }
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
@@ -5726,6 +5789,7 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
 {
     GpRegion *clip;
     GpStatus status;
+    GpMatrix device_to_world;
 
     TRACE("(%p, %p)\n", graphics, region);
 
@@ -5738,6 +5802,14 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
         return status;
 
+    get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
+    status = GdipTransformRegion(clip, &device_to_world);
+    if (status != Ok)
+    {
+        GdipDeleteRegion(clip);
+        return status;
+    }
+
     /* free everything except root node and header */
     delete_element(&region->node);
     memcpy(region, clip, sizeof(GpRegion));
diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c
index b7af6a7..15c5e43 100644
--- a/dlls/gdiplus/tests/graphics.c
+++ b/dlls/gdiplus/tests/graphics.c
@@ -4594,7 +4594,6 @@ static void test_clipping(void)
 
     status = GdipGetClipBounds(graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
        "expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
 
@@ -4604,7 +4603,6 @@ todo_wine
     expect(Ok, status);
     status = GdipGetRegionBounds(region, graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
        "expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
 
@@ -4617,7 +4615,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45,
        "expected 45,20-95,45, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -4626,7 +4623,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -4670,7 +4666,6 @@ todo_wine
 
     status = GdipGetClipBounds(graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
        "expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
 
@@ -4680,7 +4675,6 @@ todo_wine
     expect(Ok, status);
     status = GdipGetRegionBounds(region, graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
        "expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
 
@@ -4693,7 +4687,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45,
        "expected 45,20-95,45, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -4702,7 +4695,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -4746,7 +4738,6 @@ todo_wine
 
     status = GdipGetClipBounds(graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok((rect.X == 13.75 && rect.Y == 4.375 && rect.Width == 18.75 && rect.Height == 9.375) ||
        broken(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0) /* before Win7 */,
        "expected 13.75,4.375-18.75,9.375, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
@@ -4757,8 +4748,9 @@ todo_wine
     expect(Ok, status);
     status = GdipGetRegionBounds(region, graphics, &rect);
     expect(Ok, status);
-todo_wine
     ok((rect.X == 13.75 && rect.Y == 4.375 && rect.Width == 18.75 && rect.Height == 9.375) ||
+       /* rounding under Wine is slightly different */
+       (rect.X == 14.0 && rect.Y == 4.0 && rect.Width == 19.0 && rect.Height == 10.0) /* Wine */ ||
        broken(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0) /* before Win7 */,
        "expected 13.75,4.375-18.75,9.375, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
 
@@ -4771,8 +4763,9 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 14 && rc.top == 5 && rc.right == 33 && rc.bottom == 14) ||
+       /* rounding under Wine is slightly different */
+       (rc.left == 14 && rc.top == 4 && rc.right == 33 && rc.bottom == 14) /* Wine */ ||
        broken(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45) /* before Win7 */,
        "expected 14,5-33,14, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -4781,7 +4774,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
       broken(rc.left == 267 && rc.top == 267 && rc.right == 534 && rc.bottom == 534) /* before Win7 */,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -4962,7 +4954,6 @@ static void test_clipping_2(void)
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 7200 && rc.top == 7200 && rc.right == 14400 && rc.bottom == 14400) ||
        broken(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) /* before Win7 */,
        "expected 7200,7200-14400,14400, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -4971,7 +4962,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 9600 && rc.top == 9600 && rc.right == 19200 && rc.bottom == 19200) ||
        broken(rc.left == 134 && rc.top == 134 && rc.right == 267 && rc.bottom == 267) /* before Win7 */,
        "expected 9600,9600-19200,19200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -5053,7 +5043,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 75 && rc.top == 75 && rc.right == 150 && rc.bottom == 150) ||
        broken(rc.left == 2 && rc.top == 2 && rc.right == 3 && rc.bottom == 3) /* before Win7 */,
        "expected 75,75-150,150, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -5062,7 +5051,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
        broken(rc.left == 2 && rc.top == 2 && rc.right == 3 && rc.bottom == 3) /* before Win7 */,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -5099,7 +5087,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 65 && rc.top == 65 && rc.right == 140 && rc.bottom == 140,
        "expected 65,65-140,140, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -5107,7 +5094,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -5137,7 +5123,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 300 && rc.top == 150 && rc.right == 600 && rc.bottom == 300,
        "expected 300,150-600,300, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -5145,7 +5130,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
     DeleteObject(hrgn);
@@ -5170,7 +5154,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 150 && rc.top == 75 && rc.right == 300 && rc.bottom == 150) ||
        broken(rc.left == 300 && rc.top == 150 && rc.right == 600 && rc.bottom == 300) /* before Win7 */,
        "expected 150,75-300,150, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
@@ -5179,7 +5162,6 @@ todo_wine
     expect(Ok, status);
     ret = GetRgnBox(hrgn, &rc);
     ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
-todo_wine
     ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
        broken(rc.left == 200 && rc.top == 200 && rc.right == 400 && rc.bottom == 400) /* before Win7 */,
        "expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
-- 
1.8.3.3




More information about the wine-patches mailing list