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 # use the fill as a mask
mask = Image.new("1", self.im.size) mask = Image.new("1", self.im.size)
mask_ink = self._getink(1)[0] mask_ink = self._getink(1)[0]
draw = Draw(mask)
fill_im = mask.copy()
draw = Draw(fill_im)
draw.draw.draw_polygon(xy, mask_ink, 1) draw.draw.draw_polygon(xy, mask_ink, 1)
ink_im = mask.copy() self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im)
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)
def regular_polygon( def regular_polygon(
self, self,

View File

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

View File

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