gdi32: Implement clipping of diagonal lines.

This commit is contained in:
Huw Davies 2011-04-27 14:24:08 +01:00 committed by Alexandre Julliard
parent bab850389a
commit e248615ae7
2 changed files with 328 additions and 36 deletions

View File

@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdlib.h>
#include "gdi_private.h"
#include "dibdrv.h"
@ -106,6 +108,31 @@ static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
(pt->y >= rect->top) && (pt->y < rect->bottom));
}
#define Y_INCREASING_MASK 0x0f
#define X_INCREASING_MASK 0xc3
#define X_MAJOR_MASK 0x99
#define POS_SLOPE_MASK 0x33
static inline BOOL is_xmajor(DWORD octant)
{
return octant & X_MAJOR_MASK;
}
static inline BOOL is_pos_slope(DWORD octant)
{
return octant & POS_SLOPE_MASK;
}
static inline BOOL is_x_increasing(DWORD octant)
{
return octant & X_INCREASING_MASK;
}
static inline BOOL is_y_increasing(DWORD octant)
{
return octant & Y_INCREASING_MASK;
}
/**********************************************************************
* get_octant_number
*
@ -143,65 +170,276 @@ static void solid_pen_line_callback(INT x, INT y, LPARAM lparam)
return;
}
static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, void (* callback)(INT,INT,LPARAM), LPARAM lParam)
#define OUT_LEFT 1
#define OUT_RIGHT 2
#define OUT_TOP 4
#define OUT_BOTTOM 8
static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
{
INT xadd = 1, yadd = 1;
INT err, erradd;
INT cnt;
INT dx = x2 - x1;
INT dy = y2 - y1;
DWORD octant = get_octant_mask(dx, dy);
INT bias = 0;
DWORD out = 0;
if(pt->x < clip->left) out |= OUT_LEFT;
else if(pt->x >= clip->right) out |= OUT_RIGHT;
if(pt->y < clip->top) out |= OUT_TOP;
else if(pt->y >= clip->bottom) out |= OUT_BOTTOM;
/* Octants 3, 5, 6 and 8 take a bias */
if(octant & 0xb4) bias = 1;
return out;
}
if (dx < 0)
typedef struct
{
unsigned int dx, dy;
int bias;
DWORD octant;
} bres_params;
/******************************************************************************
* clip_line
*
* Clips the start and end points to a rectangle.
*
* Note, this treats the end point like the start point. If the
* caller doesn't want it displayed, it should exclude it. If the end
* point is clipped out, then the likelihood is that the new end point
* should be displayed.
*
* Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
*
* This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
* however the Bresenham error term is defined differently so the equations
* will also differ.
*
* For x major lines we have 2dy >= err + bias > 2dy - 2dx
* 0 >= err + bias - 2dy > -2dx
*
* Note dx, dy, m and n are all +ve.
*
* Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
* err = 2dy - dx + 2mdy - 2ndx
* 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
* 0 >= 2mdy - 2ndx + bias - dx > -2dx
* which of course will give exactly one solution for n,
* so looking at the >= inequality
* n >= (2mdy + bias - dx) / 2dx
* n = ceiling((2mdy + bias - dx) / 2dx)
* = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
*
* Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
* solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
* 0 >= 2mdy - 2ndx + bias - dx > -2dx
* 2mdy > 2ndx - bias - dx
* m > (2ndx - bias - dx) / 2dy
* m = floor((2ndx - bias - dx) / 2dy) + 1
* m = (2ndx - bias - dx) / 2dy + 1
*
* Moving end pt from x2 to x2 - m, we need to figure out y2 - n
* err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
* = 2dy - dx - 2mdy + 2ndx
* 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
* 0 >= 2ndx - 2mdy + bias - dx > -2dx
* again exactly one solution.
* 2ndx <= 2mdy - bias + dx
* n = floor((2mdy - bias + dx) / 2dx)
* = (2mdy - bias + dx) / 2dx
*
* Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
* mininizing m to include all of the points at y = y2 - n. As above:
* 0 >= 2ndx - 2mdy + bias - dx > -2dx
* 2mdy >= 2ndx + bias - dx
* m = ceiling((2ndx + bias - dx) / 2dy)
* = (2ndx + bias - dx - 1) / 2dy + 1
*
* For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
*
* Moving start point from y1 to y1 + n find x1 + m
* m = (2ndx + bias + dy - 1) / 2dy
*
* Moving start point from x1 to x1 + m find y1 + n
* n = (2mdy - bias - dy) / 2ndx + 1
*
* Moving end point from y2 to y2 - n find x1 - m
* m = (2ndx - bias + dy) / 2dy
*
* Moving end point from x2 to x2 - m find y2 - n
* n = (2mdy + bias - dy - 1) / 2dx + 1
*/
static int clip_line(const POINT *start, const POINT *end, const RECT *clip,
const bres_params *params, POINT *pt1, POINT *pt2)
{
unsigned int n, m;
BOOL clipped = FALSE;
DWORD start_oc, end_oc;
const int bias = params->bias;
const unsigned int dx = params->dx;
const unsigned int dy = params->dy;
const unsigned int two_dx = params->dx * 2;
const unsigned int two_dy = params->dy * 2;
const BOOL xmajor = is_xmajor(params->octant);
const BOOL neg_slope = !is_pos_slope(params->octant);
*pt1 = *start;
*pt2 = *end;
start_oc = calc_outcode(start, clip);
end_oc = calc_outcode(end, clip);
while(1)
{
dx = -dx;
xadd = -1;
if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
if(start_oc & end_oc) return 0; /* trivial reject */
clipped = TRUE;
if(start_oc & OUT_LEFT)
{
m = clip->left - start->x;
if(xmajor)
n = (m * two_dy + bias + dx - 1) / two_dx;
else
n = (m * two_dy - bias - dy) / two_dx + 1;
pt1->x = clip->left;
if(neg_slope) n = -n;
pt1->y = start->y + n;
start_oc = calc_outcode(pt1, clip);
}
if (dy < 0)
else if(start_oc & OUT_RIGHT)
{
dy = -dy;
yadd = -1;
m = start->x - clip->right + 1;
if(xmajor)
n = (m * two_dy + bias + dx - 1) / two_dx;
else
n = (m * two_dy - bias - dy) / two_dx + 1;
pt1->x = clip->right - 1;
if(neg_slope) n = -n;
pt1->y = start->y - n;
start_oc = calc_outcode(pt1, clip);
}
if (dx > dy) /* line is "more horizontal" */
else if(start_oc & OUT_TOP)
{
err = 2*dy - dx; erradd = 2*dy - 2*dx;
for(cnt = 0; cnt < dx; cnt++)
n = clip->top - start->y;
if(xmajor)
m = (n * two_dx - bias - dx) / two_dy + 1;
else
m = (n * two_dx + bias + dy - 1) / two_dy;
pt1->y = clip->top;
if(neg_slope) m = -m;
pt1->x = start->x + m;
start_oc = calc_outcode(pt1, clip);
}
else if(start_oc & OUT_BOTTOM)
{
n = start->y - clip->bottom + 1;
if(xmajor)
m = (n * two_dx - bias - dx) / two_dy + 1;
else
m = (n * two_dx + bias + dy - 1) / two_dy;
pt1->y = clip->bottom - 1;
if(neg_slope) m = -m;
pt1->x = start->x - m;
start_oc = calc_outcode(pt1, clip);
}
else if(end_oc & OUT_LEFT)
{
m = clip->left - end->x;
if(xmajor)
n = (m * two_dy - bias + dx) / two_dx;
else
n = (m * two_dy + bias - dy - 1) / two_dx + 1;
pt2->x = clip->left;
if(neg_slope) n = -n;
pt2->y = end->y + n;
end_oc = calc_outcode(pt2, clip);
}
else if(end_oc & OUT_RIGHT)
{
m = end->x - clip->right + 1;
if(xmajor)
n = (m * two_dy - bias + dx) / two_dx;
else
n = (m * two_dy + bias - dy - 1) / two_dx + 1;
pt2->x = clip->right - 1;
if(neg_slope) n = -n;
pt2->y = end->y - n;
end_oc = calc_outcode(pt2, clip);
}
else if(end_oc & OUT_TOP)
{
n = clip->top - end->y;
if(xmajor)
m = (n * two_dx + bias - dx - 1) / two_dy + 1;
else
m = (n * two_dx - bias + dy) / two_dy;
pt2->y = clip->top;
if(neg_slope) m = -m;
pt2->x = end->x + m;
end_oc = calc_outcode(pt2, clip);
}
else if(end_oc & OUT_BOTTOM)
{
n = end->y - clip->bottom + 1;
if(xmajor)
m = (n * two_dx + bias - dx - 1) / two_dy + 1;
else
m = (n * two_dx - bias + dy) / two_dy;
pt2->y = clip->bottom - 1;
if(neg_slope) m = -m;
pt2->x = end->x - m;
end_oc = calc_outcode(pt2, clip);
}
}
}
static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, const bres_params *params, INT err,
BOOL last_pt, void (* callback)(INT,INT,LPARAM), LPARAM lParam)
{
const int xadd = is_x_increasing(params->octant) ? 1 : -1;
const int yadd = is_y_increasing(params->octant) ? 1 : -1;
INT erradd;
if (is_xmajor(params->octant)) /* line is "more horizontal" */
{
erradd = 2*params->dy - 2*params->dx;
while(x1 != x2)
{
callback(x1, y1, lParam);
if (err + bias > 0)
if (err + params->bias > 0)
{
y1 += yadd;
err += erradd;
}
else err += 2*dy;
else err += 2*params->dy;
x1 += xadd;
}
if(last_pt) callback(x1, y1, lParam);
}
else /* line is "more vertical" */
{
err = 2*dx - dy; erradd = 2*dx - 2*dy;
for(cnt = 0; cnt < dy; cnt++)
erradd = 2*params->dx - 2*params->dy;
while(y1 != y2)
{
callback(x1, y1, lParam);
if (err + bias > 0)
if (err + params->bias > 0)
{
x1 += xadd;
err += erradd;
}
else err += 2*dx;
else err += 2*params->dx;
y1 += yadd;
}
if(last_pt) callback(x1, y1, lParam);
}
}
static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
{
const WINEREGION *clip = get_wine_region(pdev->clip);
BOOL ret = TRUE;
if(start->y == end->y)
{
@ -264,15 +502,47 @@ static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
}
else
{
if(clip->numRects == 1 && pt_in_rect(&clip->extents, start) && pt_in_rect(&clip->extents, end))
/* FIXME: Optimize by moving Bresenham algorithm to the primitive functions,
or at least cache adjacent points in the callback */
bres_line_with_bias(start->x, start->y, end->x, end->y, solid_pen_line_callback, (LPARAM)pdev);
else if(clip->numRects >= 1)
ret = FALSE;
bres_params params;
INT dx = end->x - start->x;
INT dy = end->y - start->y;
INT i;
params.dx = abs(dx);
params.dy = abs(dy);
params.octant = get_octant_mask(dx, dy);
/* Octants 3, 5, 6 and 8 take a bias */
params.bias = (params.octant & 0xb4) ? 1 : 0;
for(i = 0; i < clip->numRects; i++)
{
POINT clipped_start, clipped_end;
int clip_status;
clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
if(clip_status)
{
int m = abs(clipped_start.x - start->x);
int n = abs(clipped_start.y - start->y);
int err;
BOOL last_pt = FALSE;
if(is_xmajor(params.octant))
err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
else
err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
err, last_pt, solid_pen_line_callback, (LPARAM)pdev);
if(clip_status == 2) break; /* completely unclipped, so we can finish */
}
}
}
release_wine_region(pdev->clip);
return ret;
return TRUE;
}
/***********************************************************************

View File

@ -82,6 +82,7 @@ static const char *sha1_graphics_a8r8g8b8[] =
"17b2c177bdce5e94433574a928bda5c94a8cdfa5",
"fe6cc678fb13a3ead67839481bf22348adc69f52",
"d51bd330cec510cdccf5394328bd8e5411901e9e",
"df4aebf98d91f11be560dd232123b3ae327303d7",
"f2af53dd073a09b1031d0032d28da35c82adc566",
NULL
};
@ -179,7 +180,7 @@ static const RECT hline_clips[] =
{ 10, 134, 101, 134}, /* r end on l edgecase */
{ 10, 136, 100, 136}, /* r end on l edgecase clipped */
{199, 138, 220, 138}, /* l end on r edgecase */
{200, 140, 220, 200} /* l end on r edgecase clipped */
{200, 140, 220, 140} /* l end on r edgecase clipped */
};
static const RECT vline_clips[] =
@ -201,6 +202,17 @@ static const RECT vline_clips[] =
{140, 200, 140, 220} /* t end on b edgecase clipped */
};
static const RECT line_clips[] =
{
{ 90, 110, 310, 120},
{ 90, 120, 295, 130},
{ 90, 190, 110, 240}, /* totally clipped, moving outcodes */
{ 90, 130, 100, 135}, /* totally clipped, end pt on l edge */
{ 90, 132, 101, 137}, /* end pt just inside l edge */
{200, 140, 210, 141}, /* totally clipped, start pt on r edge */
{199, 142, 210, 143} /* start pt just inside r edge */
};
static const RECT patblt_clips[] =
{
{120, 120, 140, 126}, /* unclipped */
@ -252,6 +264,7 @@ static void draw_graphics(HDC hdc, BITMAPINFO *bmi, BYTE *bits, const char ***sh
compare_hash(bmi, bits, sha1, "h and v solid lines");
memset(bits, 0xcc, dib_size);
/* diagonal lines */
SetROP2(hdc, R2_COPYPEN);
for(i = 0; i < 16; i++)
{
@ -320,6 +333,15 @@ static void draw_graphics(HDC hdc, BITMAPINFO *bmi, BYTE *bits, const char ***sh
compare_hash(bmi, bits, sha1, "clipped solid vlines");
memset(bits, 0xcc, dib_size);
for(i = 0; i < sizeof(line_clips)/sizeof(line_clips[0]); i++)
{
MoveToEx(hdc, line_clips[i].left, line_clips[i].top, NULL);
LineTo(hdc, line_clips[i].right, line_clips[i].bottom);
}
compare_hash(bmi, bits, sha1, "clipped solid diagonal lines");
memset(bits, 0xcc, dib_size);
/* clipped PatBlt */
for(i = 0; i < sizeof(patblt_clips) / sizeof(patblt_clips[0]); i++)
{
PatBlt(hdc, patblt_clips[i].left, patblt_clips[i].top,