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