[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, ®ion);
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(®ion->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