Alexandre Julliard : gdi32: Use a better algorithm for CreateRoundRectRgn.

Alexandre Julliard julliard at winehq.org
Fri Jan 6 15:31:32 CST 2012


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

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Fri Jan  6 12:53:20 2012 +0100

gdi32: Use a better algorithm for CreateRoundRectRgn.

---

 dlls/gdi32/dibdrv/objects.c |    4 +-
 dlls/gdi32/region.c         |  123 ++++++++++++++++++-------------------------
 2 files changed, 53 insertions(+), 74 deletions(-)

diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c
index e18d715..de2339a 100644
--- a/dlls/gdi32/dibdrv/objects.c
+++ b/dlls/gdi32/dibdrv/objects.c
@@ -1512,7 +1512,7 @@ static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close
 
     if (pdev->pen_join == PS_JOIN_ROUND || pdev->pen_endcap == PS_ENDCAP_ROUND)
         round_cap = CreateEllipticRgn( -(pdev->pen_width / 2), -(pdev->pen_width / 2),
-                                       (pdev->pen_width + 1) / 2, (pdev->pen_width + 1) / 2 );
+                                       (pdev->pen_width + 1) / 2 + 1, (pdev->pen_width + 1) / 2 + 1 );
 
     if (close)
         wide_line_segments( pdev, num, pts, TRUE, 0, num, &pts[0], &pts[0], round_cap, total );
@@ -1538,7 +1538,7 @@ static BOOL dashed_wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOO
 
     if (pdev->pen_join == PS_JOIN_ROUND || pdev->pen_endcap == PS_ENDCAP_ROUND)
         round_cap = CreateEllipticRgn( -(pdev->pen_width / 2), -(pdev->pen_width / 2),
-                                       (pdev->pen_width + 1) / 2, (pdev->pen_width + 1) / 2 );
+                                       (pdev->pen_width + 1) / 2 + 1, (pdev->pen_width + 1) / 2 + 1);
 
     start = 0;
     cur_len = 0;
diff --git a/dlls/gdi32/region.c b/dlls/gdi32/region.c
index 66ca1c3..336e56e 100644
--- a/dlls/gdi32/region.c
+++ b/dlls/gdi32/region.c
@@ -754,101 +754,80 @@ HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
 {
     RGNOBJ * obj;
     HRGN hrgn = 0;
-    int asq, bsq, d, xd, yd;
-    RECT rect;
+    int a, b, i, x, y, asq, bsq, dx, dy, err;
+    RECT *rects;
 
       /* Make the dimensions sensible */
 
     if (left > right) { INT tmp = left; left = right; right = tmp; }
     if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
+    /* the region is for the rectangle interior, but only at right and bottom for some reason */
+    right--;
+    bottom--;
 
-    ellipse_width = abs(ellipse_width);
-    ellipse_height = abs(ellipse_height);
-
-      /* Check parameters */
-
-    if (ellipse_width > right-left) ellipse_width = right-left;
-    if (ellipse_height > bottom-top) ellipse_height = bottom-top;
+    ellipse_width = min( right - left, abs( ellipse_width ));
+    ellipse_height = min( bottom - top, abs( ellipse_height ));
 
       /* Check if we can do a normal rectangle instead */
 
     if ((ellipse_width < 2) || (ellipse_height < 2))
         return CreateRectRgn( left, top, right, bottom );
 
-      /* Create region */
-
-    d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
     if (!(obj = HeapAlloc( GetProcessHeap(), 0, sizeof(*obj) ))) return 0;
-    if (!init_region( &obj->rgn, d ))
-    {
-        HeapFree( GetProcessHeap(), 0, obj );
-        return 0;
-    }
+    obj->rgn.size = ellipse_height;
+    obj->rgn.numRects = ellipse_height;
+    obj->rgn.extents.left   = left;
+    obj->rgn.extents.top    = top;
+    obj->rgn.extents.right  = right;
+    obj->rgn.extents.bottom = bottom;
 
-      /* Ellipse algorithm, based on an article by K. Porter */
-      /* in DDJ Graphics Programming Column, 8/89 */
+    obj->rgn.rects = rects = HeapAlloc( GetProcessHeap(), 0, obj->rgn.size * sizeof(RECT) );
+    if (!rects) goto done;
 
-    asq = ellipse_width * ellipse_width / 4;        /* a^2 */
-    bsq = ellipse_height * ellipse_height / 4;      /* b^2 */
-    d = bsq - asq * ellipse_height / 2 + asq / 4;   /* b^2 - a^2b + a^2/4 */
-    xd = 0;
-    yd = asq * ellipse_height;                      /* 2a^2b */
+    /* based on an algorithm by Alois Zingl */
 
-    rect.left   = left + ellipse_width / 2;
-    rect.right  = right - ellipse_width / 2;
+    a = ellipse_width - 1;
+    b = ellipse_height - 1;
+    asq = 8 * a * a;
+    bsq = 8 * b * b;
+    dx  = 4 * b * b * (1 - a);
+    dy  = 4 * a * a * (1 + (b % 2));
+    err = dx + dy + a * a * (b % 2);
 
-      /* Loop to draw first half of quadrant */
+    x = 0;
+    y = ellipse_height / 2;
 
-    while (xd < yd)
+    rects[y].left = left;
+    rects[y].right = right;
+
+    while (x <= ellipse_width / 2)
     {
-	if (d > 0)  /* if nearest pixel is toward the center */
-	{
-	      /* move toward center */
-	    rect.top = top++;
-	    rect.bottom = rect.top + 1;
-	    if (!REGION_UnionRectWithRegion( &rect, &obj->rgn )) goto done;
-	    rect.top = --bottom;
-	    rect.bottom = rect.top + 1;
-	    if (!REGION_UnionRectWithRegion( &rect, &obj->rgn )) goto done;
-	    yd -= 2*asq;
-	    d  -= yd;
-	}
-	rect.left--;        /* next horiz point */
-	rect.right++;
-	xd += 2*bsq;
-	d  += bsq + xd;
+        int e2 = 2 * err;
+        if (e2 >= dx)
+        {
+            x++;
+            err += dx += bsq;
+        }
+        if (e2 <= dy)
+        {
+            y++;
+            err += dy += asq;
+            rects[y].left = left + x;
+            rects[y].right = right - x;
+        }
     }
-
-      /* Loop to draw second half of quadrant */
-
-    d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
-    while (yd >= 0)
+    for (i = 0; i < ellipse_height / 2; i++)
     {
-	  /* next vertical point */
-	rect.top = top++;
-	rect.bottom = rect.top + 1;
-	if (!REGION_UnionRectWithRegion( &rect, &obj->rgn )) goto done;
-	rect.top = --bottom;
-	rect.bottom = rect.top + 1;
-	if (!REGION_UnionRectWithRegion( &rect, &obj->rgn )) goto done;
-	if (d < 0)   /* if nearest pixel is outside ellipse */
-	{
-	    rect.left--;     /* move away from center */
-	    rect.right++;
-	    xd += 2*bsq;
-	    d  += xd;
-	}
-	yd -= 2*asq;
-	d  += asq - yd;
+        rects[i].left = rects[b - i].left;
+        rects[i].right = rects[b - i].right;
+        rects[i].top = top + i;
+        rects[i].bottom = rects[i].top + 1;
     }
-
-      /* Add the inside rectangle */
-
-    if (top <= bottom)
+    rects[i - 1].bottom = bottom - ellipse_height + i;  /* extend to bottom of rectangle */
+    for (; i < ellipse_height; i++)
     {
-	rect.top = top;
-	rect.bottom = bottom;
-	if (!REGION_UnionRectWithRegion( &rect, &obj->rgn )) goto done;
+        rects[i].top = bottom - ellipse_height + i;
+        rects[i].bottom = rects[i].top + 1;
     }
 
     hrgn = alloc_gdi_handle( &obj->header, OBJ_REGION, &region_funcs );




More information about the wine-cvs mailing list