gdi32: Add clipping for horizontal and vertical solid lines.
diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c
index 1ad2f30..2d006aa 100644
--- a/dlls/gdi32/dibdrv/objects.c
+++ b/dlls/gdi32/dibdrv/objects.c
@@ -100,10 +100,10 @@
     }
 }
 
-static inline BOOL pt_in_rect( const RECT *rect, POINT pt )
+static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
 {
-    return ((pt.x >= rect->left) && (pt.x < rect->right) &&
-            (pt.y >= rect->top) && (pt.y < rect->bottom));
+    return ((pt->x >= rect->left) && (pt->x < rect->right) &&
+            (pt->y >= rect->top) && (pt->y < rect->bottom));
 }
 
 static void WINAPI solid_pen_line_callback(INT x, INT y, LPARAM lparam)
@@ -121,36 +121,79 @@
 
 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
 {
-    RECT rc;
-    DC *dc = get_dibdrv_dc( &pdev->dev );
+    const WINEREGION *clip = get_wine_region(pdev->clip);
+    BOOL ret = TRUE;
 
-    if(get_clip_region(dc) || !pt_in_rect(&dc->vis_rect, *start) || !pt_in_rect(&dc->vis_rect, *end))
-        return FALSE;
-
-    rc.left   = start->x;
-    rc.top    = start->y;
-    rc.right  = end->x;
-    rc.bottom = end->y;
-
-    if(rc.top == rc.bottom)
+    if(start->y == end->y)
     {
-        order_end_points(&rc.left, &rc.right);
-        rc.bottom++;
-        pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rc, pdev->pen_and, pdev->pen_xor);
+        RECT rect;
+        int i;
+
+        rect.left   = start->x;
+        rect.top    = start->y;
+        rect.right  = end->x;
+        rect.bottom = end->y + 1;
+        order_end_points(&rect.left, &rect.right);
+        for(i = 0; i < clip->numRects; i++)
+        {
+            if(clip->rects[i].top >= rect.bottom) break;
+            if(clip->rects[i].bottom <= rect.top) continue;
+            /* Optimize the unclipped case */
+            if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
+            {
+                pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
+                break;
+            }
+            if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
+            {
+                RECT tmp = rect;
+                tmp.left = max(rect.left, clip->rects[i].left);
+                tmp.right = min(rect.right, clip->rects[i].right);
+                pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
+            }
+        }
     }
-    else if(rc.left == rc.right)
+    else if(start->x == end->x)
     {
-        order_end_points(&rc.top, &rc.bottom);
-        rc.right++;
-        pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rc, pdev->pen_and, pdev->pen_xor);
+        RECT rect;
+        int i;
+
+        rect.left   = start->x;
+        rect.top    = start->y;
+        rect.right  = end->x + 1;
+        rect.bottom = end->y;
+        order_end_points(&rect.top, &rect.bottom);
+        for(i = 0; i < clip->numRects; i++)
+        {
+            /* Optimize unclipped case */
+            if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
+               clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
+            {
+                pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
+                break;
+            }
+            if(clip->rects[i].top >= rect.bottom) break;
+            if(clip->rects[i].bottom <= rect.top) continue;
+            if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
+            {
+                RECT tmp = rect;
+                tmp.top = max(rect.top, clip->rects[i].top);
+                tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
+                pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
+            }
+        }
     }
     else
     {
-        /* FIXME: Optimize by moving Bresenham algorithm to the primitive functions,
-           or at least cache adjacent points in the callback */
-        LineDDA(start->x, start->y, end->x, end->y, solid_pen_line_callback, (LPARAM)pdev);
+        if(clip->numRects == 1 && pt_in_rect(&clip->extents, start) && pt_in_rect(&clip->extents, end))
+            /* FIXME: Optimize by moving Bresenham algorithm to the primitive functions,
+               or at least cache adjacent points in the callback */
+            LineDDA(start->x, start->y, end->x, end->y, solid_pen_line_callback, (LPARAM)pdev);
+        else if(clip->numRects >= 1)
+            ret = FALSE;
     }
-    return TRUE;
+    release_wine_region(pdev->clip);
+    return ret;
 }
 
 /***********************************************************************
diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h
index e126aa1..c0d57d9 100644
--- a/dlls/gdi32/gdi_private.h
+++ b/dlls/gdi32/gdi_private.h
@@ -553,6 +553,19 @@
 extern INT mirror_region( HRGN dst, HRGN src, INT width ) DECLSPEC_HIDDEN;
 extern BOOL REGION_FrameRgn( HRGN dest, HRGN src, INT x, INT y ) DECLSPEC_HIDDEN;
 
+typedef struct
+{
+    INT size;
+    INT numRects;
+    RECT *rects;
+    RECT extents;
+} WINEREGION;
+extern const WINEREGION *get_wine_region(HRGN rgn) DECLSPEC_HIDDEN;
+static inline void release_wine_region(HRGN rgn)
+{
+    GDI_ReleaseObj(rgn);
+}
+
 /* null driver entry points */
 extern BOOL CDECL nulldrv_AbortPath( PHYSDEV dev ) DECLSPEC_HIDDEN;
 extern BOOL CDECL nulldrv_AngleArc( PHYSDEV dev, INT x, INT y, DWORD radius, FLOAT start, FLOAT sweep ) DECLSPEC_HIDDEN;
diff --git a/dlls/gdi32/region.c b/dlls/gdi32/region.c
index b37211f..cd3c9dd 100644
--- a/dlls/gdi32/region.c
+++ b/dlls/gdi32/region.c
@@ -105,13 +105,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(region);
 
-typedef struct {
-    INT size;
-    INT numRects;
-    RECT *rects;
-    RECT extents;
-} WINEREGION;
-
   /* GDI logical region object */
 typedef struct
 {
@@ -924,6 +917,20 @@
 				 rect->bottom - rect->top );
 }
 
+/*********************************************************************
+ *   get_wine_region
+ *
+ * Return the region data without making a copy.  The caller
+ * must not alter anything and must call GDI_ReleaseObj() when
+ * they have finished with the data.
+ */
+const WINEREGION *get_wine_region(HRGN rgn)
+{
+    RGNOBJ *obj = GDI_GetObjPtr( rgn, OBJ_REGION );
+    if(!obj) return NULL;
+    return &obj->rgn;
+}
+
 /***********************************************************************
  *           GetRegionData   (GDI32.@)
  *
diff --git a/dlls/gdi32/tests/dib.c b/dlls/gdi32/tests/dib.c
index a6a1156..90eb008 100644
--- a/dlls/gdi32/tests/dib.c
+++ b/dlls/gdi32/tests/dib.c
@@ -78,6 +78,8 @@
     "a3cadd34d95d3d5cc23344f69aab1c2e55935fcf",
     "2426172d9e8fec27d9228088f382ef3c93717da9",
     "9e8f27ca952cdba01dbf25d07c34e86a7820c012",
+    "76343ceb04e6295e0560019249d3c0318a23c8a6",
+    "6ecee6ba1c06dcb6b70ff42a8ea2df7803847860",
     "17b2c177bdce5e94433574a928bda5c94a8cdfa5",
     NULL
 };
@@ -147,12 +149,43 @@
     HeapFree(GetProcessHeap(), 0, hash);
 }
 
+static const RECT hline_clips[] =
+{
+    {120, 120, 140, 120}, /* unclipped */
+    {100, 122, 140, 122}, /* l edgecase */
+    { 99, 124, 140, 124}, /* l edgecase clipped */
+    {120, 126, 200, 126}, /* r edgecase */
+    {120, 128, 201, 128}, /* r edgecase clipped */
+    { 99, 130, 201, 130}, /* l and r clipped */
+    {120, 100, 140, 100}, /* t edgecase */
+    {120,  99, 140,  99}, /* t edgecase clipped */
+    {120, 199, 140, 199}, /* b edgecase */
+    {120, 200, 140, 200}, /* b edgecase clipped */
+    {120, 132, 310, 132}  /* inside two clip rects */
+};
+
+static const RECT vline_clips[] =
+{
+    {120, 120, 120, 140}, /* unclipped */
+    {100, 120, 100, 140}, /* l edgecase */
+    { 99, 120,  99, 140}, /* l edgecase clipped */
+    {199, 120, 199, 140}, /* r edgecase */
+    {200, 120, 200, 140}, /* r edgecase clipped */
+    {122,  99, 122, 201}, /* t and b clipped */
+    {124, 100, 124, 140}, /* t edgecase */
+    {126,  99, 126, 140}, /* t edgecase clipped */
+    {128, 120, 128, 200}, /* b edgecase */
+    {130, 120, 130, 201}, /* b edgecase clipped */
+    {132,  12, 132, 140}  /* inside two clip rects */
+};
+
 static void draw_graphics(HDC hdc, BITMAPINFO *bmi, BYTE *bits, const char ***sha1)
 {
     DWORD dib_size = get_dib_size(bmi);
     HPEN solid_pen, orig_pen;
     HBRUSH solid_brush, orig_brush;
     INT i, y;
+    HRGN hrgn, hrgn2;
 
     memset(bits, 0xcc, dib_size);
     compare_hash(bmi, bits, sha1, "empty");
@@ -188,6 +221,33 @@
     compare_hash(bmi, bits, sha1, "diagonal solid lines");
     memset(bits, 0xcc, dib_size);
 
+    hrgn = CreateRectRgn(10, 10, 200, 20);
+    hrgn2 = CreateRectRgn(100, 100, 200, 200);
+    CombineRgn(hrgn, hrgn, hrgn2, RGN_OR);
+    SetRectRgn(hrgn2, 290, 100, 300, 200);
+    CombineRgn(hrgn, hrgn, hrgn2, RGN_OR);
+    ExtSelectClipRgn(hdc, hrgn, RGN_COPY);
+    DeleteObject(hrgn2);
+    DeleteObject(hrgn);
+
+    for(i = 0; i < sizeof(hline_clips)/sizeof(hline_clips[0]); i++)
+    {
+        MoveToEx(hdc, hline_clips[i].left, hline_clips[i].top, NULL);
+        LineTo(hdc, hline_clips[i].right, hline_clips[i].bottom);
+    }
+    compare_hash(bmi, bits, sha1, "clipped solid hlines");
+    memset(bits, 0xcc, dib_size);
+
+    for(i = 0; i < sizeof(vline_clips)/sizeof(vline_clips[0]); i++)
+    {
+        MoveToEx(hdc, vline_clips[i].left, vline_clips[i].top, NULL);
+        LineTo(hdc, vline_clips[i].right, vline_clips[i].bottom);
+    }
+    compare_hash(bmi, bits, sha1, "clipped solid vlines");
+    memset(bits, 0xcc, dib_size);
+
+    ExtSelectClipRgn(hdc, NULL, RGN_COPY);
+
     solid_brush = CreateSolidBrush(RGB(0x33, 0xaa, 0xff));
     orig_brush = SelectObject(hdc, solid_brush);