gdi32: Implement dashed wide lines.

This commit is contained in:
Alexandre Julliard 2012-01-04 15:05:00 +01:00
parent bc5428600b
commit c82d2a034f
1 changed files with 138 additions and 6 deletions

View File

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