From c82d2a034f918a297915e39784ddfe949b37919c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 4 Jan 2012 15:05:00 +0100 Subject: [PATCH] gdi32: Implement dashed wide lines. --- dlls/gdi32/dibdrv/objects.c | 144 ++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 6 deletions(-) diff --git a/dlls/gdi32/dibdrv/objects.c b/dlls/gdi32/dibdrv/objects.c index 69333463e68..51a49cc44c5 100644 --- a/dlls/gdi32/dibdrv/objects.c +++ b/dlls/gdi32/dibdrv/objects.c @@ -1523,6 +1523,125 @@ static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close return TRUE; } +static BOOL dashed_wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN total) +{ + int i, start, cur_len, initial_num = 0; + POINT initial_point, start_point, end_point; + HRGN round_cap = 0; + + assert( total != 0 ); /* wide pens should always be drawn through a region */ + assert( num >= 2 ); + + /* skip empty segments */ + while (num > 2 && pts[0].x == pts[1].x && pts[0].y == pts[1].y) { pts++; num--; } + while (num > 2 && pts[num - 1].x == pts[num - 2].x && pts[num - 1].y == pts[num - 2].y) num--; + + 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 ); + + start = 0; + cur_len = 0; + start_point = pts[0]; + + for (i = 0; i < (close ? num : num - 1); i++) + { + const POINT *pt_1 = pts + i; + const POINT *pt_2 = pts + ((close && i == num - 1) ? 0 : i + 1); + int dx = pt_2->x - pt_1->x; + int dy = pt_2->y - pt_1->y; + + if (dx == 0 && dy == 0) continue; + + if (dy == 0) + { + if (abs( dx ) - cur_len < pdev->dash_pos.left_in_dash) + { + skip_dash( pdev, abs( dx ) - cur_len ); + cur_len = 0; + continue; + } + cur_len += pdev->dash_pos.left_in_dash; + dx = (dx > 0) ? cur_len : -cur_len; + } + else if (dx == 0) + { + if (abs( dy ) - cur_len < pdev->dash_pos.left_in_dash) + { + skip_dash( pdev, abs( dy ) - cur_len ); + cur_len = 0; + continue; + } + cur_len += pdev->dash_pos.left_in_dash; + dy = (dy > 0) ? cur_len : -cur_len; + } + else + { + double len = hypot( dx, dy ); + + if (len - cur_len < pdev->dash_pos.left_in_dash) + { + skip_dash( pdev, len - cur_len ); + cur_len = 0; + continue; + } + cur_len += pdev->dash_pos.left_in_dash; + dx = dx * cur_len / len; + dy = dy * cur_len / len; + } + end_point.x = pt_1->x + dx; + end_point.y = pt_1->y + dy; + + if (pdev->dash_pos.mark) + { + if (!initial_num && close) /* this is the first dash, save it for later */ + { + initial_num = i - start + 1; + initial_point = end_point; + } + else wide_line_segments( pdev, num, pts, FALSE, start, i - start + 1, + &start_point, &end_point, round_cap, total ); + } + if (!initial_num) initial_num = -1; /* no need to close it */ + + skip_dash( pdev, pdev->dash_pos.left_in_dash ); + start_point = end_point; + start = i; + i--; /* go on with the same segment */ + } + + if (pdev->dash_pos.mark) /* we have a final dash */ + { + int count; + + if (initial_num > 0) + { + count = num - start + initial_num; + end_point = initial_point; + } + else if (close) + { + count = num - start; + end_point = pts[0]; + } + else + { + count = num - start - 1; + end_point = pts[num - 1]; + } + wide_line_segments( pdev, num, pts, FALSE, start, count, + &start_point, &end_point, round_cap, total ); + } + else if (initial_num > 0) /* initial dash only */ + { + wide_line_segments( pdev, num, pts, FALSE, 0, initial_num, + &pts[0], &initial_point, round_cap, total ); + } + + if (round_cap) DeleteObject( round_cap ); + return TRUE; +} + static const dash_pattern dash_patterns_cosmetic[4] = { {2, {18, 6}, 24}, /* PS_DASH */ @@ -1550,11 +1669,21 @@ static inline void set_dash_pattern( dash_pattern *pattern, DWORD count, DWORD * if (pattern->count % 2) pattern->total_len *= 2; } -static inline void scale_dash_pattern( dash_pattern *pattern, DWORD scale ) +static inline void scale_dash_pattern( dash_pattern *pattern, DWORD scale, DWORD endcap ) { DWORD i; + for (i = 0; i < pattern->count; i++) pattern->dashes[i] *= scale; pattern->total_len *= scale; + + if (endcap != PS_ENDCAP_FLAT) /* shrink the dashes to leave room for the caps */ + { + for (i = 0; i < pattern->count; i += 2) + { + pattern->dashes[i] -= scale; + pattern->dashes[i + 1] += scale; + } + } } static inline int get_pen_device_width( dibdrv_physdev *pdev, int width ) @@ -1971,9 +2100,13 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen, const struct brush_pattern *patte case PS_DASHDOTDOT: if (logpen.lopnStyle & PS_GEOMETRIC) { - if (pdev->pen_width > 1) break; /* not supported yet */ - pdev->pen_lines = dashed_pen_lines; pdev->pen_pattern = dash_patterns_geometric[pdev->pen_style - 1]; + if (pdev->pen_width > 1) + { + scale_dash_pattern( &pdev->pen_pattern, pdev->pen_width, pdev->pen_endcap ); + pdev->pen_lines = dashed_wide_pen_lines; + } + else pdev->pen_lines = dashed_pen_lines; pdev->defer &= ~DEFER_PEN; break; } @@ -2004,10 +2137,9 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen, const struct brush_pattern *patte break; case PS_USERSTYLE: - if (pdev->pen_width > 1) break; /* not supported yet */ - pdev->pen_lines = dashed_pen_lines; + pdev->pen_lines = (pdev->pen_width == 1) ? dashed_pen_lines : dashed_wide_pen_lines; set_dash_pattern( &pdev->pen_pattern, elp->elpNumEntries, elp->elpStyleEntry ); - if (!(logpen.lopnStyle & PS_GEOMETRIC)) scale_dash_pattern( &pdev->pen_pattern, 3 ); + if (!(logpen.lopnStyle & PS_GEOMETRIC)) scale_dash_pattern( &pdev->pen_pattern, 3, PS_ENDCAP_FLAT ); pdev->defer &= ~DEFER_PEN; break; }