Huw Davies : gdi32: Add support for wide cosmetic pens.

Alexandre Julliard julliard at winehq.org
Fri Dec 9 14:41:37 CST 2011


Module: wine
Branch: master
Commit: e155a8054bff9e139116ceaeec272b5cbf0a3d3b
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=e155a8054bff9e139116ceaeec272b5cbf0a3d3b

Author: Huw Davies <huw at codeweavers.com>
Date:   Fri Dec  9 13:47:27 2011 +0000

gdi32: Add support for wide cosmetic pens.

---

 dlls/gdi32/dibdrv/dibdrv.h  |    3 +-
 dlls/gdi32/dibdrv/objects.c |  185 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 185 insertions(+), 3 deletions(-)

diff --git a/dlls/gdi32/dibdrv/dibdrv.h b/dlls/gdi32/dibdrv/dibdrv.h
index 1bf0696..6f01a5e 100644
--- a/dlls/gdi32/dibdrv/dibdrv.h
+++ b/dlls/gdi32/dibdrv/dibdrv.h
@@ -85,7 +85,8 @@ typedef struct dibdrv_physdev
 
     /* pen */
     COLORREF pen_colorref;
-    DWORD pen_color, pen_and, pen_xor;
+    DWORD pen_color, pen_and, pen_xor, pen_endcap, pen_join;
+    int pen_width;
     dash_pattern pen_pattern;
     dash_pos dash_pos;
     BOOL   (* pen_lines)(struct dibdrv_physdev *pdev, int num, POINT *pts, BOOL close);
diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c
index 9c85094..14f1260 100644
--- a/dlls/gdi32/dibdrv/objects.c
+++ b/dlls/gdi32/dibdrv/objects.c
@@ -1017,6 +1017,166 @@ static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close
     return TRUE;
 }
 
+static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
+{
+    HRGN cap;
+
+    switch (pdev->pen_endcap)
+    {
+    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 */
+        return;
+    }
+
+    CombineRgn( region, region, cap, RGN_OR );
+    DeleteObject( cap );
+    return;
+}
+
+static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
+{
+    HRGN join;
+
+    switch (pdev->pen_join)
+    {
+    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;
+    }
+
+    CombineRgn( region, region, join, RGN_OR );
+    DeleteObject( join );
+    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;
+    HRGN total, segment;
+
+    assert( num >= 2 );
+
+    total = CreateRectRgn( 0, 0, 0, 0 );
+
+    if (!close) num--;
+    for (i = 0; i < num; i++)
+    {
+        const POINT *pt_1 = pts + i;
+        const POINT *pt_2 = pts + ((close && i == num - 1) ? 0 : i + 1);
+        int dx = pt_2->x - pt_1->x;
+        int dy = pt_2->y - pt_1->y;
+        RECT rect;
+
+        if (dx == 0 && dy == 0) continue;
+
+        if (dy == 0)
+        {
+            rect.left = min( pt_1->x, pt_2->x );
+            rect.right = rect.left + abs( dx );
+            rect.top = pt_1->y - pdev->pen_width / 2;
+            rect.bottom = rect.top + pdev->pen_width;
+            segment = CreateRectRgnIndirect( &rect );
+        }
+        else if (dx == 0)
+        {
+            rect.top = min( pt_1->y, pt_2->y );
+            rect.bottom = rect.top + abs( dy );
+            rect.left = pt_1->x - pdev->pen_width / 2;
+            rect.right = rect.left + pdev->pen_width;
+            segment = CreateRectRgnIndirect( &rect );
+        }
+        else
+        {
+            double len = hypot( dx, dy );
+            double width_x, width_y;
+            POINT seg_pts[4];
+            POINT wide_half, narrow_half;
+
+            width_x = pdev->pen_width * abs( dy ) / len;
+            width_y = pdev->pen_width * abs( dx ) / len;
+
+            narrow_half.x = round( width_x / 2 );
+            narrow_half.y = round( width_y / 2 );
+            wide_half.x   = round( (width_x + 1) / 2 );
+            wide_half.y   = round( (width_y + 1) / 2 );
+
+            if (dx < 0)
+            {
+                wide_half.y   = -wide_half.y;
+                narrow_half.y = -narrow_half.y;
+            }
+
+            if (dy < 0)
+            {
+                POINT tmp = narrow_half; narrow_half = wide_half; wide_half = tmp;
+                wide_half.x   = -wide_half.x;
+                narrow_half.x = -narrow_half.x;
+            }
+
+            seg_pts[0].x = pt_1->x - narrow_half.x;
+            seg_pts[0].y = pt_1->y + narrow_half.y;
+            seg_pts[1].x = pt_1->x + wide_half.x;
+            seg_pts[1].y = pt_1->y - wide_half.y;
+            seg_pts[2].x = pt_2->x + wide_half.x;
+            seg_pts[2].y = pt_2->y - wide_half.y;
+            seg_pts[3].x = pt_2->x - narrow_half.x;
+            seg_pts[3].y = pt_2->y + narrow_half.y;
+
+            segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
+        }
+
+        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 (i == num - 1)
+        {
+            if (close) add_join( pdev, total, pt_2 );
+            else add_cap( pdev, total, pt_2 );
+        }
+
+    }
+    return total;
+}
+
+static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
+{
+    const WINEREGION *data;
+    rop_mask color;
+    HRGN region;
+
+    color.and = pdev->pen_and;
+    color.xor = pdev->pen_xor;
+
+    region = get_wide_lines_region( pdev, num, pts, close );
+
+    if (CombineRgn( region, region, pdev->clip, RGN_AND ) != ERROR)
+    {
+        data = get_wine_region( region );
+        solid_rects( &pdev->dib, data->numRects, data->rects, &color, NULL );
+        release_wine_region( region );
+    }
+
+    DeleteObject( region );
+    return TRUE;
+}
+
 static const dash_pattern dash_patterns[5] =
 {
     {0, {0}, 0},                  /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
@@ -1026,6 +1186,21 @@ static const dash_pattern dash_patterns[5] =
     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
 };
 
+static inline int get_pen_device_width( dibdrv_physdev *pdev, LOGPEN *pen )
+{
+    int width = pen->lopnWidth.x;
+
+    if (pen->lopnStyle & PS_GEOMETRIC && width > 1)
+    {
+        POINT pts[2];
+        pts[0].x = pts[0].y = pts[1].y = 0;
+        pts[1].x = width;
+        LPtoDP( pdev->dev.hdc, pts, 2 );
+        width = max( abs( pts[1].x - pts[0].x ), 1 );
+    }
+    return width;
+}
+
 /***********************************************************************
  *           dibdrv_SelectPen
  */
@@ -1058,6 +1233,10 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
         HeapFree( GetProcessHeap(), 0, elp );
     }
 
+    pdev->pen_join   = logpen.lopnStyle & PS_JOIN_MASK;
+    pdev->pen_endcap = logpen.lopnStyle & PS_ENDCAP_MASK;
+    pdev->pen_width  = get_pen_device_width( pdev, &logpen );
+
     if (hpen == GetStockObject( DC_PEN ))
         logpen.lopnColor = GetDCPenColor( dev->hdc );
 
@@ -1075,8 +1254,10 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
     {
     case PS_SOLID:
         if(logpen.lopnStyle & PS_GEOMETRIC) break;
-        if(logpen.lopnWidth.x > 1) break;
-        pdev->pen_lines = solid_pen_lines;
+        if(pdev->pen_width <= 1)
+            pdev->pen_lines = solid_pen_lines;
+        else
+            pdev->pen_lines = wide_pen_lines;
         pdev->defer &= ~DEFER_PEN;
         break;
 




More information about the wine-cvs mailing list