From 3e51dd751568730d500b7ed7402fbf308a99f028 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 6 Jan 2012 12:53:20 +0100 Subject: [PATCH] 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 e18d715866e..de2339afb75 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 66ca1c38b03..336e56e621b 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, ®ion_funcs ); -- 2.11.4.GIT