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, ®ion_funcs );
More information about the wine-cvs
mailing list