gdi32: Use a better algorithm for CreateRoundRectRgn.

This commit is contained in:
Alexandre Julliard 2012-01-06 12:53:20 +01:00
parent a86b765a50
commit 3e51dd7515
2 changed files with 58 additions and 79 deletions

View File

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

View File

@ -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 ))
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;
obj->rgn.rects = rects = HeapAlloc( GetProcessHeap(), 0, obj->rgn.size * sizeof(RECT) );
if (!rects) goto done;
/* based on an algorithm by Alois Zingl */
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);
x = 0;
y = ellipse_height / 2;
rects[y].left = left;
rects[y].right = right;
while (x <= ellipse_width / 2)
{
HeapFree( GetProcessHeap(), 0, obj );
return 0;
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;
}
}
/* Ellipse algorithm, based on an article by K. Porter */
/* in DDJ Graphics Programming Column, 8/89 */
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 */
rect.left = left + ellipse_width / 2;
rect.right = right - ellipse_width / 2;
/* Loop to draw first half of quadrant */
while (xd < yd)
for (i = 0; i < ellipse_height / 2; i++)
{
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;
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;
}
/* Loop to draw second half of quadrant */
d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
while (yd >= 0)
rects[i - 1].bottom = bottom - ellipse_height + i; /* extend to bottom of rectangle */
for (; i < ellipse_height; 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;
}
/* Add the inside rectangle */
if (top <= bottom)
{
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 );