gdi32: Add support for wide cosmetic pens.

This commit is contained in:
Huw Davies 2011-12-09 13:47:27 +00:00 committed by Alexandre Julliard
parent c368e55196
commit e155a8054b
2 changed files with 185 additions and 3 deletions

View File

@ -85,7 +85,8 @@ typedef struct dibdrv_physdev
/* pen */
COLORREF pen_colorref;
DWORD pen_color, pen_and, pen_xor;
DWORD pen_color, pen_and, pen_xor, pen_endcap, pen_join;
int pen_width;
dash_pattern pen_pattern;
dash_pos dash_pos;
BOOL (* pen_lines)(struct dibdrv_physdev *pdev, int num, POINT *pts, BOOL close);

View File

@ -1017,6 +1017,166 @@ static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close
return TRUE;
}
static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
{
HRGN cap;
switch (pdev->pen_endcap)
{
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 */
return;
}
CombineRgn( region, region, cap, RGN_OR );
DeleteObject( cap );
return;
}
static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
{
HRGN join;
switch (pdev->pen_join)
{
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;
}
CombineRgn( region, region, join, RGN_OR );
DeleteObject( join );
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;
HRGN total, segment;
assert( num >= 2 );
total = CreateRectRgn( 0, 0, 0, 0 );
if (!close) num--;
for (i = 0; i < num; 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;
RECT rect;
if (dx == 0 && dy == 0) continue;
if (dy == 0)
{
rect.left = min( pt_1->x, pt_2->x );
rect.right = rect.left + abs( dx );
rect.top = pt_1->y - pdev->pen_width / 2;
rect.bottom = rect.top + pdev->pen_width;
segment = CreateRectRgnIndirect( &rect );
}
else if (dx == 0)
{
rect.top = min( pt_1->y, pt_2->y );
rect.bottom = rect.top + abs( dy );
rect.left = pt_1->x - pdev->pen_width / 2;
rect.right = rect.left + pdev->pen_width;
segment = CreateRectRgnIndirect( &rect );
}
else
{
double len = hypot( dx, dy );
double width_x, width_y;
POINT seg_pts[4];
POINT wide_half, narrow_half;
width_x = pdev->pen_width * abs( dy ) / len;
width_y = pdev->pen_width * abs( dx ) / len;
narrow_half.x = round( width_x / 2 );
narrow_half.y = round( width_y / 2 );
wide_half.x = round( (width_x + 1) / 2 );
wide_half.y = round( (width_y + 1) / 2 );
if (dx < 0)
{
wide_half.y = -wide_half.y;
narrow_half.y = -narrow_half.y;
}
if (dy < 0)
{
POINT tmp = narrow_half; narrow_half = wide_half; wide_half = tmp;
wide_half.x = -wide_half.x;
narrow_half.x = -narrow_half.x;
}
seg_pts[0].x = pt_1->x - narrow_half.x;
seg_pts[0].y = pt_1->y + narrow_half.y;
seg_pts[1].x = pt_1->x + wide_half.x;
seg_pts[1].y = pt_1->y - wide_half.y;
seg_pts[2].x = pt_2->x + wide_half.x;
seg_pts[2].y = pt_2->y - wide_half.y;
seg_pts[3].x = pt_2->x - narrow_half.x;
seg_pts[3].y = pt_2->y + narrow_half.y;
segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
}
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 (i == num - 1)
{
if (close) add_join( pdev, total, pt_2 );
else add_cap( pdev, total, pt_2 );
}
}
return total;
}
static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
{
const WINEREGION *data;
rop_mask color;
HRGN region;
color.and = pdev->pen_and;
color.xor = pdev->pen_xor;
region = get_wide_lines_region( pdev, num, pts, close );
if (CombineRgn( region, region, pdev->clip, RGN_AND ) != ERROR)
{
data = get_wine_region( region );
solid_rects( &pdev->dib, data->numRects, data->rects, &color, NULL );
release_wine_region( region );
}
DeleteObject( region );
return TRUE;
}
static const dash_pattern dash_patterns[5] =
{
{0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
@ -1026,6 +1186,21 @@ static const dash_pattern dash_patterns[5] =
{6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
};
static inline int get_pen_device_width( dibdrv_physdev *pdev, LOGPEN *pen )
{
int width = pen->lopnWidth.x;
if (pen->lopnStyle & PS_GEOMETRIC && width > 1)
{
POINT pts[2];
pts[0].x = pts[0].y = pts[1].y = 0;
pts[1].x = width;
LPtoDP( pdev->dev.hdc, pts, 2 );
width = max( abs( pts[1].x - pts[0].x ), 1 );
}
return width;
}
/***********************************************************************
* dibdrv_SelectPen
*/
@ -1058,6 +1233,10 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
HeapFree( GetProcessHeap(), 0, elp );
}
pdev->pen_join = logpen.lopnStyle & PS_JOIN_MASK;
pdev->pen_endcap = logpen.lopnStyle & PS_ENDCAP_MASK;
pdev->pen_width = get_pen_device_width( pdev, &logpen );
if (hpen == GetStockObject( DC_PEN ))
logpen.lopnColor = GetDCPenColor( dev->hdc );
@ -1075,8 +1254,10 @@ HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
{
case PS_SOLID:
if(logpen.lopnStyle & PS_GEOMETRIC) break;
if(logpen.lopnWidth.x > 1) break;
pdev->pen_lines = solid_pen_lines;
if(pdev->pen_width <= 1)
pdev->pen_lines = solid_pen_lines;
else
pdev->pen_lines = wide_pen_lines;
pdev->defer &= ~DEFER_PEN;
break;