gdi32: Implement Ellipse and RoundRect, using line segments for now.

This commit is contained in:
Alexandre Julliard 2012-01-06 12:55:15 +01:00
parent 3e51dd7515
commit 5ff285629e
3 changed files with 172 additions and 4 deletions

View File

@ -575,7 +575,7 @@ const struct gdi_dc_funcs dib_driver =
NULL, /* pDeleteObject */
dibdrv_DescribePixelFormat, /* pDescribePixelFormat */
NULL, /* pDeviceCapabilities */
NULL, /* pEllipse */
dibdrv_Ellipse, /* pEllipse */
NULL, /* pEndDoc */
NULL, /* pEndPage */
NULL, /* pEndPath */
@ -642,7 +642,7 @@ const struct gdi_dc_funcs dib_driver =
dibdrv_Rectangle, /* pRectangle */
NULL, /* pResetDC */
NULL, /* pRestoreDC */
NULL, /* pRoundRect */
dibdrv_RoundRect, /* pRoundRect */
NULL, /* pSaveDC */
NULL, /* pScaleViewportExt */
NULL, /* pScaleWindowExt */

View File

@ -112,14 +112,15 @@ extern BOOL dibdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blend ) DECLSPEC_HIDDEN;
extern DWORD dibdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
struct bitblt_coords *src, struct bitblt_coords *dst, BLENDFUNCTION func ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
void *grad_array, ULONG ngrad, ULONG mode ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
const RECT *rect, LPCWSTR str, UINT count, const INT *dx ) DECLSPEC_HIDDEN;
extern DWORD dibdrv_GetImage( PHYSDEV dev, HBITMAP hbitmap, BITMAPINFO *info,
struct gdi_image_bits *bits, struct bitblt_coords *src ) DECLSPEC_HIDDEN;
extern COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color ) DECLSPEC_HIDDEN;
extern COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
void *grad_array, ULONG ngrad, ULONG mode ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN hrgn ) DECLSPEC_HIDDEN;
@ -132,6 +133,8 @@ extern DWORD dibdrv_PutImage( PHYSDEV dev, HBITMAP hbitmap, HRGN clip, BITMAP
const struct gdi_image_bits *bits, struct bitblt_coords *src,
struct bitblt_coords *dst, DWORD rop ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom ) DECLSPEC_HIDDEN;
extern BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
INT ellipse_width, INT ellipse_height ) DECLSPEC_HIDDEN;
extern HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern ) DECLSPEC_HIDDEN;
extern HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen, const struct brush_pattern *pattern ) DECLSPEC_HIDDEN;
extern COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color ) DECLSPEC_HIDDEN;

View File

@ -57,6 +57,43 @@ static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom,
return rect;
}
/* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
/* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
static int ellipse_first_quadrant( int width, int height, POINT *data )
{
const int a = width - 1;
const int b = height - 1;
const int asq = 8 * a * a;
const int bsq = 8 * b * b;
int dx = 4 * b * b * (1 - a);
int dy = 4 * a * a * (1 + (b % 2));
int err = dx + dy + a * a * (b % 2);
int pos = 0;
POINT pt;
pt.x = a;
pt.y = height / 2;
/* based on an algorithm by Alois Zingl */
while (pt.x >= width / 2)
{
int e2 = 2 * err;
data[pos++] = pt;
if (e2 >= dx)
{
pt.x--;
err += dx += bsq;
}
if (e2 <= dy)
{
pt.y++;
err += dy += asq;
}
}
return pos;
}
/* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
static const BYTE ramp[17] =
@ -399,6 +436,14 @@ done:
return TRUE;
}
/***********************************************************************
* dibdrv_Ellipse
*/
BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
{
return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
}
/***********************************************************************
* dibdrv_GetNearestColor
*/
@ -717,6 +762,126 @@ BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
return ret;
}
/***********************************************************************
* dibdrv_RoundRect
*/
BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
INT ellipse_width, INT ellipse_height )
{
dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
RECT rect = get_device_rect( dev->hdc, left, top, right, bottom, TRUE );
POINT pt[2], *points;
int i, end, count;
BOOL ret = TRUE;
HRGN outline = 0, interior = 0;
if (rect.left == rect.right || rect.top == rect.bottom) return TRUE;
if (pdev->pen_style == PS_INSIDEFRAME)
{
rect.left += pdev->pen_width / 2;
rect.top += pdev->pen_width / 2;
rect.right -= (pdev->pen_width - 1) / 2;
rect.bottom -= (pdev->pen_width - 1) / 2;
}
pt[0].x = pt[0].y = 0;
pt[1].x = ellipse_width;
pt[1].y = ellipse_height;
LPtoDP( dev->hdc, pt, 2 );
ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
if (ellipse_width <= 2|| ellipse_height <= 2)
return dibdrv_Rectangle( dev, left, top, right, bottom );
points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
if (!points) return FALSE;
if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
{
HeapFree( GetProcessHeap(), 0, points );
return FALSE;
}
if (pdev->brush.style != BS_NULL &&
!(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
ellipse_width, ellipse_height )))
{
HeapFree( GetProcessHeap(), 0, points );
return FALSE;
}
if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
/* if not using a region, paint the interior first so the outline can overlap it */
if (interior && !outline)
{
ret = brush_region( pdev, interior );
DeleteObject( interior );
interior = 0;
}
count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
{
for (i = 0; i < count; i++)
{
points[i].x = rect.right - ellipse_width + points[i].x;
points[i].y = rect.bottom - ellipse_height + points[i].y;
}
}
else
{
for (i = 0; i < count; i++)
{
points[i].x = rect.right - ellipse_width + points[i].x;
points[i].y = rect.top + ellipse_height - 1 - points[i].y;
}
}
/* horizontal symmetry */
end = 2 * count - 1;
/* avoid duplicating the midpoint */
if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
for (i = 0; i < count; i++)
{
points[end - i].x = rect.left + rect.right - 1 - points[i].x;
points[end - i].y = points[i].y;
}
count = end + 1;
/* vertical symmetry */
end = 2 * count - 1;
/* avoid duplicating the midpoint */
if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
for (i = 0; i < count; i++)
{
points[end - i].x = points[i].x;
points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
}
count = end + 1;
reset_dash_origin( pdev );
pdev->pen_lines( pdev, count, points, TRUE, outline );
if (interior)
{
CombineRgn( interior, interior, outline, RGN_DIFF );
ret = brush_region( pdev, interior );
DeleteObject( interior );
}
if (outline)
{
if (ret) ret = pen_region( pdev, outline );
DeleteObject( outline );
}
HeapFree( GetProcessHeap(), 0, points );
return ret;
}
/***********************************************************************
* dibdrv_SetPixel
*/