Use mask in C when drawing wide polygon lines (#8984)

This commit is contained in:
Andrew Murray 2025-06-10 11:46:12 +10:00 committed by GitHub
parent 05636dca17
commit 7b163cc35d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 116 additions and 52 deletions

View File

@ -365,22 +365,10 @@ class ImageDraw:
# use the fill as a mask
mask = Image.new("1", self.im.size)
mask_ink = self._getink(1)[0]
fill_im = mask.copy()
draw = Draw(fill_im)
draw = Draw(mask)
draw.draw.draw_polygon(xy, mask_ink, 1)
ink_im = mask.copy()
draw = Draw(ink_im)
width = width * 2 - 1
draw.draw.draw_polygon(xy, mask_ink, 0, width)
mask.paste(ink_im, mask=fill_im)
im = Image.new(self.mode, self.im.size)
draw = Draw(im)
draw.draw.draw_polygon(xy, ink, 0, width)
self.im.paste(im.im, (0, 0) + im.size, mask.im)
self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im)
def regular_polygon(
self,

View File

@ -3220,7 +3220,8 @@ _draw_lines(ImagingDrawObject *self, PyObject *args) {
(int)p[3],
&ink,
width,
self->blend
self->blend,
NULL
) < 0) {
free(xy);
return NULL;
@ -3358,7 +3359,10 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
int ink;
int fill = 0;
int width = 0;
if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
ImagingObject *maskp = NULL;
if (!PyArg_ParseTuple(
args, "Oi|iiO!", &data, &ink, &fill, &width, &Imaging_Type, &maskp
)) {
return NULL;
}
@ -3388,8 +3392,16 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
free(xy);
if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) <
0) {
if (ImagingDrawPolygon(
self->image->image,
n,
ixy,
&ink,
fill,
width,
self->blend,
maskp ? maskp->image : NULL
) < 0) {
free(ixy);
return NULL;
}

View File

@ -63,7 +63,7 @@ typedef struct {
} Edge;
/* Type used in "polygon*" functions */
typedef void (*hline_handler)(Imaging, int, int, int, int);
typedef void (*hline_handler)(Imaging, int, int, int, int, Imaging);
static inline void
point8(Imaging im, int x, int y, int ink) {
@ -103,7 +103,7 @@ point32rgba(Imaging im, int x, int y, int ink) {
}
static inline void
hline8(Imaging im, int x0, int y0, int x1, int ink) {
hline8(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
int pixelwidth;
if (y0 >= 0 && y0 < im->ysize) {
@ -119,15 +119,30 @@ hline8(Imaging im, int x0, int y0, int x1, int ink) {
}
if (x0 <= x1) {
pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;
memset(
im->image8[y0] + x0 * pixelwidth, (UINT8)ink, (x1 - x0 + 1) * pixelwidth
);
if (mask == NULL) {
memset(
im->image8[y0] + x0 * pixelwidth,
(UINT8)ink,
(x1 - x0 + 1) * pixelwidth
);
} else {
UINT8 *p = im->image8[y0];
while (x0 <= x1) {
if (mask->image8[y0][x0]) {
p[x0 * pixelwidth] = ink;
if (pixelwidth == 2) {
p[x0 * pixelwidth + 1] = ink;
}
}
x0++;
}
}
}
}
}
static inline void
hline32(Imaging im, int x0, int y0, int x1, int ink) {
hline32(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
INT32 *p;
if (y0 >= 0 && y0 < im->ysize) {
@ -143,13 +158,16 @@ hline32(Imaging im, int x0, int y0, int x1, int ink) {
}
p = im->image32[y0];
while (x0 <= x1) {
p[x0++] = ink;
if (mask == NULL || mask->image8[y0][x0]) {
p[x0] = ink;
}
x0++;
}
}
}
static inline void
hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
hline32rgba(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
unsigned int tmp;
if (y0 >= 0 && y0 < im->ysize) {
@ -167,9 +185,11 @@ hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;
UINT8 *in = (UINT8 *)&ink;
while (x0 <= x1) {
out[0] = BLEND(in[3], out[0], in[0], tmp);
out[1] = BLEND(in[3], out[1], in[1], tmp);
out[2] = BLEND(in[3], out[2], in[2], tmp);
if (mask == NULL || mask->image8[y0][x0]) {
out[0] = BLEND(in[3], out[0], in[0], tmp);
out[1] = BLEND(in[3], out[1], in[1], tmp);
out[2] = BLEND(in[3], out[2], in[2], tmp);
}
x0++;
out += 4;
}
@ -407,7 +427,14 @@ x_cmp(const void *x0, const void *x1) {
static void
draw_horizontal_lines(
Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline
Imaging im,
int n,
Edge *e,
int ink,
int *x_pos,
int y,
hline_handler hline,
Imaging mask
) {
int i;
for (i = 0; i < n; i++) {
@ -429,7 +456,7 @@ draw_horizontal_lines(
}
}
(*hline)(im, xmin, e[i].ymin, xmax, ink);
(*hline)(im, xmin, e[i].ymin, xmax, ink, mask);
*x_pos = xmax + 1;
}
}
@ -439,7 +466,9 @@ draw_horizontal_lines(
* Filled polygon draw function using scan line algorithm.
*/
static inline int
polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline) {
polygon_generic(
Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, Imaging mask
) {
Edge **edge_table;
float *xx;
int edge_count = 0;
@ -469,7 +498,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h
}
if (e[i].ymin == e[i].ymax) {
if (hasAlpha != 1) {
(*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);
(*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink, mask);
}
continue;
}
@ -557,7 +586,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h
// Line would be before the current position
continue;
}
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
if (x_end < x_pos) {
// Line would be before the current position
continue;
@ -573,13 +602,13 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h
continue;
}
}
(*hline)(im, x_start, ymin, x_end, ink);
(*hline)(im, x_start, ymin, x_end, ink, mask);
x_pos = x_end + 1;
}
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
} else {
for (i = 1; i < j; i += 2) {
(*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink);
(*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink, mask);
}
}
}
@ -623,7 +652,7 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1) {
typedef struct {
void (*point)(Imaging im, int x, int y, int ink);
void (*hline)(Imaging im, int x0, int y0, int x1, int ink);
void (*hline)(Imaging im, int x0, int y0, int x1, int ink, Imaging mask);
void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
} DRAW;
@ -674,7 +703,15 @@ ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink_, in
int
ImagingDrawWideLine(
Imaging im, int x0, int y0, int x1, int y1, const void *ink_, int width, int op
Imaging im,
int x0,
int y0,
int x1,
int y1,
const void *ink_,
int width,
int op,
Imaging mask
) {
DRAW *draw;
INT32 ink;
@ -714,7 +751,7 @@ ImagingDrawWideLine(
add_edge(e + 2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);
add_edge(e + 3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);
polygon_generic(im, 4, e, ink, 0, draw->hline);
polygon_generic(im, 4, e, ink, 0, draw->hline, mask);
}
return 0;
}
@ -757,7 +794,7 @@ ImagingDrawRectangle(
}
for (y = y0; y <= y1; y++) {
draw->hline(im, x0, y, x1, ink);
draw->hline(im, x0, y, x1, ink, NULL);
}
} else {
@ -766,8 +803,8 @@ ImagingDrawRectangle(
width = 1;
}
for (i = 0; i < width; i++) {
draw->hline(im, x0, y0 + i, x1, ink);
draw->hline(im, x0, y1 - i, x1, ink);
draw->hline(im, x0, y0 + i, x1, ink, NULL);
draw->hline(im, x0, y1 - i, x1, ink, NULL);
draw->line(im, x1 - i, y0 + width, x1 - i, y1 - width + 1, ink);
draw->line(im, x0 + i, y0 + width, x0 + i, y1 - width + 1, ink);
}
@ -778,7 +815,14 @@ ImagingDrawRectangle(
int
ImagingDrawPolygon(
Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op
Imaging im,
int count,
int *xy,
const void *ink_,
int fill,
int width,
int op,
Imaging mask
) {
int i, n, x0, y0, x1, y1;
DRAW *draw;
@ -822,7 +866,7 @@ ImagingDrawPolygon(
if (xy[i * 2] != xy[0] || xy[i * 2 + 1] != xy[1]) {
add_edge(&e[n++], xy[i * 2], xy[i * 2 + 1], xy[0], xy[1]);
}
polygon_generic(im, n, e, ink, 0, draw->hline);
polygon_generic(im, n, e, ink, 0, draw->hline, mask);
free(e);
} else {
@ -844,11 +888,12 @@ ImagingDrawPolygon(
xy[i * 2 + 3],
ink_,
width,
op
op,
mask
);
}
ImagingDrawWideLine(
im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op
im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op, mask
);
}
}
@ -1519,7 +1564,9 @@ ellipseNew(
ellipse_init(&st, a, b, width);
int32_t X0, Y, X1;
while (ellipse_next(&st, &X0, &Y, &X1) != -1) {
draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
draw->hline(
im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
);
}
return 0;
}
@ -1554,7 +1601,9 @@ clipEllipseNew(
int32_t X0, Y, X1;
int next_code;
while ((next_code = clip_ellipse_next(&st, &X0, &Y, &X1)) >= 0) {
draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
draw->hline(
im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
);
}
clip_ellipse_free(&st);
return next_code == -1 ? 0 : -1;
@ -1972,7 +2021,7 @@ ImagingDrawOutline(
DRAWINIT();
polygon_generic(im, outline->count, outline->edges, ink, 0, draw->hline);
polygon_generic(im, outline->count, outline->edges, ink, 0, draw->hline, NULL);
return 0;
}

View File

@ -510,7 +510,15 @@ extern int
ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink, int op);
extern int
ImagingDrawWideLine(
Imaging im, int x0, int y0, int x1, int y1, const void *ink, int width, int op
Imaging im,
int x0,
int y0,
int x1,
int y1,
const void *ink,
int width,
int op,
Imaging mask
);
extern int
ImagingDrawPieslice(
@ -530,7 +538,14 @@ extern int
ImagingDrawPoint(Imaging im, int x, int y, const void *ink, int op);
extern int
ImagingDrawPolygon(
Imaging im, int points, int *xy, const void *ink, int fill, int width, int op
Imaging im,
int points,
int *xy,
const void *ink,
int fill,
int width,
int op,
Imaging mask
);
extern int
ImagingDrawRectangle(