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);