mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-27 02:16:19 +03:00
Rewrite of the polygon_generic function
The (previously refactored) polygon_generic function didn't draw consistent polygons (equilateral polygons were not equilateral nor symmetrical). The most notable changes are: * The horizontal edges are searched for when finding the polygon boundaries, drawn and discarded from the edge list used to detect intersections. * The intersections are now checked and calculated from the current value of the scan line (ymin) instead of in the middle (ymin + 0.5). * Because the change in the scan line behavior, we should duplicate the intersections in the maximum Y value of an edge or there will be draw errors with concave and complex polygons. * The rounds of the X coordinates in the hline function calls are switched to draw the inner pixels. * Removed the ugly micro-optimization of qsort at the end. This implementation of the scan line algorithm may not be technically correct, it's not optimized and it have problems with some edge cases, like a wide line from (x0, y) to (x1, y + 1), therefore it should be reviewed in the future.
This commit is contained in:
parent
8739613cfb
commit
cd332fc38a
|
@ -414,62 +414,71 @@ x_cmp(const void *x0, const void *x1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill,
|
||||||
hline_handler handler)
|
hline_handler hline)
|
||||||
{
|
{
|
||||||
int i, j;
|
if (n <= 0) {
|
||||||
float *xx;
|
|
||||||
int ymin, ymax;
|
|
||||||
float y;
|
|
||||||
|
|
||||||
if (n <= 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Find upper and lower polygon boundary (within image) */
|
|
||||||
|
|
||||||
ymin = e[0].ymin;
|
|
||||||
ymax = e[0].ymax;
|
|
||||||
for (i = 1; i < n; i++) {
|
|
||||||
if (e[i].ymin < ymin) ymin = e[i].ymin;
|
|
||||||
if (e[i].ymax > ymax) ymax = e[i].ymax;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ymin < 0)
|
/* Initialize the edge table and find polygon boundaries */
|
||||||
ymin = 0;
|
Edge** edge_table = malloc(sizeof(Edge*) * n);
|
||||||
if (ymax >= im->ysize)
|
if (!edge_table) {
|
||||||
ymax = im->ysize-1;
|
|
||||||
|
|
||||||
/* Process polygon edges */
|
|
||||||
|
|
||||||
xx = malloc(n * sizeof(float));
|
|
||||||
if (!xx)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
int edge_count = 0;
|
||||||
|
int ymin = im->ysize - 1;
|
||||||
|
int ymax = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
/* This causes that the pixels of horizontal edges are drawn twice :(
|
||||||
|
* but without it there are inconsistencies in ellipses */
|
||||||
|
if (e[i].ymin == e[i].ymax) {
|
||||||
|
(*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ymin > e[i].ymin) {
|
||||||
|
ymin = e[i].ymin;
|
||||||
|
}
|
||||||
|
if (ymax < e[i].ymax) {
|
||||||
|
ymax = e[i].ymax;
|
||||||
|
}
|
||||||
|
edge_table[edge_count++] = (e + i);
|
||||||
|
}
|
||||||
|
if (ymin < 0) {
|
||||||
|
ymin = 0;
|
||||||
|
}
|
||||||
|
if (ymax >= im->ysize) {
|
||||||
|
ymax = im->ysize - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the edge table with a scan line searching for intersections */
|
||||||
|
float* xx = malloc(sizeof(float) * edge_count * 2);
|
||||||
for (; ymin <= ymax; ymin++) {
|
for (; ymin <= ymax; ymin++) {
|
||||||
y = ymin+0.5F;
|
int j = 0;
|
||||||
for (i = j = 0; i < n; i++) {
|
for (i = 0; i < edge_count; i++) {
|
||||||
if (y >= e[i].ymin && y <= e[i].ymax) {
|
Edge* current = edge_table[i];
|
||||||
if (e[i].d == 0)
|
if (ymin >= current->ymin && ymin <= current->ymax) {
|
||||||
(*handler)(im, e[i].xmin, ymin, e[i].xmax, ink);
|
xx[j++] = (ymin - current->y0) * current->dx + current->x0;
|
||||||
else
|
}
|
||||||
xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0;
|
/* Needed to draw consistent polygons */
|
||||||
|
if (ymin == current->ymax && ymin < ymax) {
|
||||||
|
xx[j] = xx[j - 1];
|
||||||
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j == 2) {
|
|
||||||
if (xx[0] < xx[1])
|
|
||||||
(*handler)(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink);
|
|
||||||
else
|
|
||||||
(*handler)(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink);
|
|
||||||
} else {
|
|
||||||
qsort(xx, j, sizeof(float), x_cmp);
|
qsort(xx, j, sizeof(float), x_cmp);
|
||||||
for (i = 0; i < j-1 ; i += 2)
|
for (i = 1; i < j; i += 2) {
|
||||||
(*handler)(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink);
|
(*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(xx);
|
free(xx);
|
||||||
|
free(edge_table);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user