Huw Davies : gdi32: Add support for geometric solid pens.
Alexandre Julliard
julliard at winehq.org
Thu Dec 22 12:35:50 CST 2011
Module: wine
Branch: master
Commit: 7c2351566ae7558b901e7dfc65840df29f94e756
URL: http://source.winehq.org/git/wine.git/?a=commit;h=7c2351566ae7558b901e7dfc65840df29f94e756
Author: Huw Davies <huw at codeweavers.com>
Date: Thu Dec 22 13:00:43 2011 +0000
gdi32: Add support for geometric solid pens.
---
dlls/gdi32/dibdrv/objects.c | 173 ++++++++++++++++++++++++++++++++++++++-----
1 files changed, 155 insertions(+), 18 deletions(-)
diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c
index f8a9f7d..281642a 100644
--- a/dlls/gdi32/dibdrv/objects.c
+++ b/dlls/gdi32/dibdrv/objects.c
@@ -999,18 +999,27 @@ static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close
return TRUE;
}
+struct face
+{
+ POINT start, end;
+ int dx, dy;
+};
+
static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
{
HRGN cap;
switch (pdev->pen_endcap)
{
+ default: FIXME( "Unknown end cap %x\n", pdev->pen_endcap );
+ /* fall through */
case PS_ENDCAP_ROUND:
cap = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
break;
- default: /* only supporting cosmetic pens so far, so always PS_ENDCAP_ROUND */
+ case PS_ENDCAP_SQUARE: /* already been handled */
+ case PS_ENDCAP_FLAT:
return;
}
@@ -1019,19 +1028,92 @@ static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
return;
}
-static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
+#define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
+
+/*******************************************************************************
+ * create_miter_region
+ *
+ * We need to calculate the intersection of two lines. We know a point
+ * on each line (a face start and the other face end point) and
+ * the direction vector of each line eg. (dx_1, dy_1).
+ *
+ * (x, y) = (x_1, y_1) + u * (dx_1, dy_1) = (x_2, y_2) + v * (dx_2, dy_2)
+ * solving (eg using Cramer's rule) gives:
+ * u = ((x_2 - x_1) dy_2 - (y_2 - y_1) dx_2) / det
+ * with det = dx_1 dy_2 - dx_2 dy_1
+ * substituting back in and simplifying gives
+ * (x, y) = a (dx_1, dy_1) - b (dx_2, dy_2)
+ * with a = (x_2 dy_2 - y_2 dx_2) / det
+ * and b = (x_1 dy_1 - y_1 dx_1) / det
+ */
+static HRGN create_miter_region( dibdrv_physdev *pdev, const POINT *pt,
+ const struct face *face_1, const struct face *face_2 )
+{
+ int det = face_1->dx * face_2->dy - face_1->dy * face_2->dx;
+ POINT pt_1, pt_2, pts[5];
+ double a, b, x, y;
+ FLOAT limit;
+
+ if (det == 0) return 0;
+
+ if (det < 0)
+ {
+ const struct face *tmp = face_1;
+ face_1 = face_2;
+ face_2 = tmp;
+ det = -det;
+ }
+
+ pt_1 = face_1->start;
+ pt_2 = face_2->end;
+
+ a = (double)((pt_2.x * face_2->dy - pt_2.y * face_2->dx)) / det;
+ b = (double)((pt_1.x * face_1->dy - pt_1.y * face_1->dx)) / det;
+
+ x = a * face_1->dx - b * face_2->dx;
+ y = a * face_1->dy - b * face_2->dy;
+
+ GetMiterLimit( pdev->dev.hdc, &limit );
+
+ if (((x - pt->x) * (x - pt->x) + (y - pt->y) * (y - pt->y)) * 4 > limit * limit * pdev->pen_width * pdev->pen_width)
+ return 0;
+
+ pts[0] = face_2->start;
+ pts[1] = face_1->start;
+ pts[2].x = round( x );
+ pts[2].y = round( y );
+ pts[3] = face_2->end;
+ pts[4] = face_1->end;
+
+ return CreatePolygonRgn( pts, 5, ALTERNATE );
+}
+
+static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt,
+ const struct face *face_1, const struct face *face_2 )
{
HRGN join;
+ POINT pts[4];
switch (pdev->pen_join)
{
+ default: FIXME( "Unknown line join %x\n", pdev->pen_join );
+ /* fall through */
case PS_JOIN_ROUND:
join = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
break;
- default: /* only supporting cosmetic pens so far, so always PS_JOIN_ROUND */
- return;
+ case PS_JOIN_MITER:
+ join = create_miter_region( pdev, pt, face_1, face_2 );
+ if (join) break;
+ /* fall through */
+ case PS_JOIN_BEVEL:
+ pts[0] = face_1->start;
+ pts[1] = face_2->end;
+ pts[2] = face_1->end;
+ pts[3] = face_2->start;
+ join = CreatePolygonRgn( pts, 4, ALTERNATE );
+ break;
}
CombineRgn( region, region, join, RGN_OR );
@@ -1039,8 +1121,6 @@ static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
return;
}
-#define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
-
static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BOOL close )
{
int i;
@@ -1058,6 +1138,11 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO
int dx = pt_2->x - pt_1->x;
int dy = pt_2->y - pt_1->y;
RECT rect;
+ struct face face_1, face_2, prev_face, first_face;
+ BOOL need_cap_1 = !close && (i == 0);
+ BOOL need_cap_2 = !close && (i == num - 1);
+ BOOL sq_cap_1 = need_cap_1 && (pdev->pen_endcap == PS_ENDCAP_SQUARE);
+ BOOL sq_cap_2 = need_cap_2 && (pdev->pen_endcap == PS_ENDCAP_SQUARE);
if (dx == 0 && dy == 0) continue;
@@ -1067,7 +1152,23 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO
rect.right = rect.left + abs( dx );
rect.top = pt_1->y - pdev->pen_width / 2;
rect.bottom = rect.top + pdev->pen_width;
+ if ((sq_cap_1 && dx > 0) || (sq_cap_2 && dx < 0)) rect.left -= pdev->pen_width / 2;
+ if ((sq_cap_2 && dx > 0) || (sq_cap_1 && dx < 0)) rect.right += pdev->pen_width / 2;
segment = CreateRectRgnIndirect( &rect );
+ if (dx > 0)
+ {
+ face_1.start.x = face_1.end.x = rect.left;
+ face_1.start.y = face_2.end.y = rect.bottom;
+ face_1.end.y = face_2.start.y = rect.top;
+ face_2.start.x = face_2.end.x = rect.right - 1;
+ }
+ else
+ {
+ face_1.start.x = face_1.end.x = rect.right;
+ face_1.start.y = face_2.end.y = rect.top;
+ face_1.end.y = face_2.start.y = rect.bottom;
+ face_2.start.x = face_2.end.x = rect.left + 1;
+ }
}
else if (dx == 0)
{
@@ -1075,7 +1176,23 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO
rect.bottom = rect.top + abs( dy );
rect.left = pt_1->x - pdev->pen_width / 2;
rect.right = rect.left + pdev->pen_width;
+ if ((sq_cap_1 && dy > 0) || (sq_cap_2 && dy < 0)) rect.top -= pdev->pen_width / 2;
+ if ((sq_cap_2 && dy > 0) || (sq_cap_1 && dy < 0)) rect.bottom += pdev->pen_width / 2;
segment = CreateRectRgnIndirect( &rect );
+ if (dy > 0)
+ {
+ face_1.start.x = face_2.end.x = rect.left;
+ face_1.start.y = face_1.end.y = rect.top;
+ face_1.end.x = face_2.start.x = rect.right;
+ face_2.start.y = face_2.end.y = rect.bottom - 1;
+ }
+ else
+ {
+ face_1.start.x = face_2.end.x = rect.right;
+ face_1.start.y = face_1.end.y = rect.bottom;
+ face_1.end.x = face_2.start.x = rect.left;
+ face_2.start.y = face_2.end.y = rect.top + 1;
+ }
}
else
{
@@ -1114,25 +1231,46 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO
seg_pts[3].x = pt_2->x - narrow_half.x;
seg_pts[3].y = pt_2->y + narrow_half.y;
+ if (sq_cap_1)
+ {
+ seg_pts[0].x -= narrow_half.y;
+ seg_pts[1].x -= narrow_half.y;
+ seg_pts[0].y -= narrow_half.x;
+ seg_pts[1].y -= narrow_half.x;
+ }
+
+ if (sq_cap_2)
+ {
+ seg_pts[2].x += wide_half.y;
+ seg_pts[3].x += wide_half.y;
+ seg_pts[2].y += wide_half.x;
+ seg_pts[3].y += wide_half.x;
+ }
+
segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
+
+ face_1.start = seg_pts[0];
+ face_1.end = seg_pts[1];
+ face_2.start = seg_pts[2];
+ face_2.end = seg_pts[3];
}
CombineRgn( total, total, segment, RGN_OR );
DeleteObject( segment );
- if (i == 0)
- {
- if (!close) add_cap( pdev, total, pt_1 );
- }
- else
- add_join( pdev, total, pt_1 );
+ if (need_cap_1) add_cap( pdev, total, pt_1 );
+ if (need_cap_2) add_cap( pdev, total, pt_2 );
- if (i == num - 1)
- {
- if (close) add_join( pdev, total, pt_2 );
- else add_cap( pdev, total, pt_2 );
- }
+ face_1.dx = face_2.dx = dx;
+ face_1.dy = face_2.dy = dy;
+ if (i == 0) first_face = face_1;
+ else add_join( pdev, total, pt_1, &prev_face, &face_1 );
+
+ if (i == num - 1 && close)
+ add_join( pdev, total, pt_2, &face_2, &first_face );
+
+ prev_face = face_2;
}
return total;
}
@@ -1235,7 +1373,6 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
switch(style)
{
case PS_SOLID:
- if(logpen.lopnStyle & PS_GEOMETRIC) break;
if(pdev->pen_width <= 1)
pdev->pen_lines = solid_pen_lines;
else
More information about the wine-cvs
mailing list