diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c index f8a9f7dc9f7..281642a4515 100644 --- a/dlls/gdi32/dibdrv/objects.c +++ b/dlls/gdi32/dibdrv/objects.c @@ -999,18 +999,27 @@ static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close return TRUE; } +struct face +{ + POINT start, end; + int dx, dy; +}; + static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt ) { HRGN cap; switch (pdev->pen_endcap) { + default: FIXME( "Unknown end cap %x\n", pdev->pen_endcap ); + /* fall through */ case PS_ENDCAP_ROUND: cap = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2, pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 ); break; - default: /* only supporting cosmetic pens so far, so always PS_ENDCAP_ROUND */ + case PS_ENDCAP_SQUARE: /* already been handled */ + case PS_ENDCAP_FLAT: return; } @@ -1019,19 +1028,92 @@ static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt ) return; } -static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt ) +#define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5) + +/******************************************************************************* + * create_miter_region + * + * We need to calculate the intersection of two lines. We know a point + * on each line (a face start and the other face end point) and + * the direction vector of each line eg. (dx_1, dy_1). + * + * (x, y) = (x_1, y_1) + u * (dx_1, dy_1) = (x_2, y_2) + v * (dx_2, dy_2) + * solving (eg using Cramer's rule) gives: + * u = ((x_2 - x_1) dy_2 - (y_2 - y_1) dx_2) / det + * with det = dx_1 dy_2 - dx_2 dy_1 + * substituting back in and simplifying gives + * (x, y) = a (dx_1, dy_1) - b (dx_2, dy_2) + * with a = (x_2 dy_2 - y_2 dx_2) / det + * and b = (x_1 dy_1 - y_1 dx_1) / det + */ +static HRGN create_miter_region( dibdrv_physdev *pdev, const POINT *pt, + const struct face *face_1, const struct face *face_2 ) +{ + int det = face_1->dx * face_2->dy - face_1->dy * face_2->dx; + POINT pt_1, pt_2, pts[5]; + double a, b, x, y; + FLOAT limit; + + if (det == 0) return 0; + + if (det < 0) + { + const struct face *tmp = face_1; + face_1 = face_2; + face_2 = tmp; + det = -det; + } + + pt_1 = face_1->start; + pt_2 = face_2->end; + + a = (double)((pt_2.x * face_2->dy - pt_2.y * face_2->dx)) / det; + b = (double)((pt_1.x * face_1->dy - pt_1.y * face_1->dx)) / det; + + x = a * face_1->dx - b * face_2->dx; + y = a * face_1->dy - b * face_2->dy; + + GetMiterLimit( pdev->dev.hdc, &limit ); + + if (((x - pt->x) * (x - pt->x) + (y - pt->y) * (y - pt->y)) * 4 > limit * limit * pdev->pen_width * pdev->pen_width) + return 0; + + pts[0] = face_2->start; + pts[1] = face_1->start; + pts[2].x = round( x ); + pts[2].y = round( y ); + pts[3] = face_2->end; + pts[4] = face_1->end; + + return CreatePolygonRgn( pts, 5, ALTERNATE ); +} + +static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt, + const struct face *face_1, const struct face *face_2 ) { HRGN join; + POINT pts[4]; switch (pdev->pen_join) { + default: FIXME( "Unknown line join %x\n", pdev->pen_join ); + /* fall through */ case PS_JOIN_ROUND: join = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2, pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 ); break; - default: /* only supporting cosmetic pens so far, so always PS_JOIN_ROUND */ - return; + case PS_JOIN_MITER: + join = create_miter_region( pdev, pt, face_1, face_2 ); + if (join) break; + /* fall through */ + case PS_JOIN_BEVEL: + pts[0] = face_1->start; + pts[1] = face_2->end; + pts[2] = face_1->end; + pts[3] = face_2->start; + join = CreatePolygonRgn( pts, 4, ALTERNATE ); + break; } CombineRgn( region, region, join, RGN_OR ); @@ -1039,8 +1121,6 @@ static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt ) return; } -#define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5) - static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BOOL close ) { int i; @@ -1058,6 +1138,11 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO int dx = pt_2->x - pt_1->x; int dy = pt_2->y - pt_1->y; RECT rect; + struct face face_1, face_2, prev_face, first_face; + BOOL need_cap_1 = !close && (i == 0); + BOOL need_cap_2 = !close && (i == num - 1); + BOOL sq_cap_1 = need_cap_1 && (pdev->pen_endcap == PS_ENDCAP_SQUARE); + BOOL sq_cap_2 = need_cap_2 && (pdev->pen_endcap == PS_ENDCAP_SQUARE); if (dx == 0 && dy == 0) continue; @@ -1067,7 +1152,23 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO rect.right = rect.left + abs( dx ); rect.top = pt_1->y - pdev->pen_width / 2; rect.bottom = rect.top + pdev->pen_width; + if ((sq_cap_1 && dx > 0) || (sq_cap_2 && dx < 0)) rect.left -= pdev->pen_width / 2; + if ((sq_cap_2 && dx > 0) || (sq_cap_1 && dx < 0)) rect.right += pdev->pen_width / 2; segment = CreateRectRgnIndirect( &rect ); + if (dx > 0) + { + face_1.start.x = face_1.end.x = rect.left; + face_1.start.y = face_2.end.y = rect.bottom; + face_1.end.y = face_2.start.y = rect.top; + face_2.start.x = face_2.end.x = rect.right - 1; + } + else + { + face_1.start.x = face_1.end.x = rect.right; + face_1.start.y = face_2.end.y = rect.top; + face_1.end.y = face_2.start.y = rect.bottom; + face_2.start.x = face_2.end.x = rect.left + 1; + } } else if (dx == 0) { @@ -1075,7 +1176,23 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO rect.bottom = rect.top + abs( dy ); rect.left = pt_1->x - pdev->pen_width / 2; rect.right = rect.left + pdev->pen_width; + if ((sq_cap_1 && dy > 0) || (sq_cap_2 && dy < 0)) rect.top -= pdev->pen_width / 2; + if ((sq_cap_2 && dy > 0) || (sq_cap_1 && dy < 0)) rect.bottom += pdev->pen_width / 2; segment = CreateRectRgnIndirect( &rect ); + if (dy > 0) + { + face_1.start.x = face_2.end.x = rect.left; + face_1.start.y = face_1.end.y = rect.top; + face_1.end.x = face_2.start.x = rect.right; + face_2.start.y = face_2.end.y = rect.bottom - 1; + } + else + { + face_1.start.x = face_2.end.x = rect.right; + face_1.start.y = face_1.end.y = rect.bottom; + face_1.end.x = face_2.start.x = rect.left; + face_2.start.y = face_2.end.y = rect.top + 1; + } } else { @@ -1114,25 +1231,46 @@ static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BO seg_pts[3].x = pt_2->x - narrow_half.x; seg_pts[3].y = pt_2->y + narrow_half.y; + if (sq_cap_1) + { + seg_pts[0].x -= narrow_half.y; + seg_pts[1].x -= narrow_half.y; + seg_pts[0].y -= narrow_half.x; + seg_pts[1].y -= narrow_half.x; + } + + if (sq_cap_2) + { + seg_pts[2].x += wide_half.y; + seg_pts[3].x += wide_half.y; + seg_pts[2].y += wide_half.x; + seg_pts[3].y += wide_half.x; + } + segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE ); + + face_1.start = seg_pts[0]; + face_1.end = seg_pts[1]; + face_2.start = seg_pts[2]; + face_2.end = seg_pts[3]; } CombineRgn( total, total, segment, RGN_OR ); DeleteObject( segment ); - if (i == 0) - { - if (!close) add_cap( pdev, total, pt_1 ); - } - else - add_join( pdev, total, pt_1 ); + if (need_cap_1) add_cap( pdev, total, pt_1 ); + if (need_cap_2) add_cap( pdev, total, pt_2 ); - if (i == num - 1) - { - if (close) add_join( pdev, total, pt_2 ); - else add_cap( pdev, total, pt_2 ); - } + face_1.dx = face_2.dx = dx; + face_1.dy = face_2.dy = dy; + if (i == 0) first_face = face_1; + else add_join( pdev, total, pt_1, &prev_face, &face_1 ); + + if (i == num - 1 && close) + add_join( pdev, total, pt_2, &face_2, &first_face ); + + prev_face = face_2; } return total; } @@ -1235,7 +1373,6 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen ) switch(style) { case PS_SOLID: - if(logpen.lopnStyle & PS_GEOMETRIC) break; if(pdev->pen_width <= 1) pdev->pen_lines = solid_pen_lines; else