diff options
Diffstat (limited to 'draw_linear_bezier_reference.c')
-rw-r--r-- | draw_linear_bezier_reference.c | 156 |
1 files changed, 82 insertions, 74 deletions
diff --git a/draw_linear_bezier_reference.c b/draw_linear_bezier_reference.c index dbfb87f..b7dab11 100644 --- a/draw_linear_bezier_reference.c +++ b/draw_linear_bezier_reference.c @@ -14,86 +14,94 @@ */ +static void +draw_cell(RASTER *restrict raster, double x1, double y1, double x2, double y2, double dx, double dy, + double cxmin, double cxmax, double rxmin, double rxmax, double rymin, double rymax, ssize_t x, size_t cellY) +{ + double cy1, cy2, ydir = (double)SIGNUM(dy); + double rightedge, opposite, midadjacent; + size_t cell; + + rightedge = cxmax; + cxmin = fmax(cxmin, rxmin); + cxmax = fmin(cxmax, rxmax); + cy1 = dx ? y1 + (cxmin - x1) * dy / dx : y1; + cy2 = dx ? y2 + (cxmax - x2) * dy / dx : y2; + cy1 = fmin(fmax(cy1, rymin), rymax); + cy2 = fmin(fmax(cy2, rymin), rymax); + + opposite = ydir * fabs(cy2 - cy1); + midadjacent = (cxmin + cxmax) / 2.0; + + if (x < 0) { + raster->cells[cellY].opposite_coverage += opposite; + raster->cells[cellY].cell_coverage += opposite; + } else if ((size_t)x >= raster->width) { + cell = cellY + (raster->width - 1); + raster->cells[cell].opposite_coverage += opposite; + } else { + cell = cellY + (size_t)x; + raster->cells[cell].opposite_coverage += opposite; + raster->cells[cell].cell_coverage += opposite * fabs(rightedge - midadjacent); + } +} + void draw_linear_bezier_reference(RASTER *restrict raster, double x1, double y1, double x2, double y2) { + ssize_t iwidth = (ssize_t)raster->width; + double width = (double)raster->width; + double height = (double)raster->height; double dx = x2 - x1; double dy = y2 - y1; - int xdir = SIGNUM(dx); - int ydir = SIGNUM(dy); - double kx = dx / dy; - double ky = dy / dx; - double prevX, prevY, x, y, cx, cy, h2x, h2y; - double xfloor, yfloor; - ssize_t cellX, cellY; - size_t cell; - - prevX = x1; - prevY = y1; - - for (; prevX != x2 || prevY != y2; prevX = x, prevY = y) { - /* Find next vertical and next hozitonal grid line */ - x = xdir < 0 ? floor(prevX) : xdir > 0 ? ceil(prevX) : prevX; - y = ydir < 0 ? floor(prevY) : ydir > 0 ? ceil(prevY) : prevY; - x = iszeroish(x - prevX) ? x + xdir : x; - y = iszeroish(y - prevY) ? y + ydir : y; - - /* Limit to the extend of the drawn line segment */ - if ((xdir < 0 && x < x2) || (xdir > 0 && x > x2)) - x = x2; - if ((ydir < 0 && y < y2) || (ydir > 0 && y > y2)) - y = y2; - - /* Find next cell edge */ - if (xdir && ydir) { - cy = y1 + (x - x1) * ky; - cx = x1 + (y - y1) * kx; - h2x = (cy - prevY) * (cy - prevY) + (x - prevX) * (x - prevX); - h2y = (cx - prevX) * (cx - prevX) + (y - prevY) * (y - prevY); - if (h2x < h2y) - y = cy; - else - x = cx; - - /* Make sure loop will exit when we reach the end */ - if ((xdir < 0 && x <= x2) || (xdir > 0 && x >= x2) || - (ydir < 0 && y <= y2) || (ydir > 0 && y >= y2)) { - x = x2; - y = y2; - } + double fymin = fmin(fmax(fmin(y1, y2), 0.0), height); + double fymax = fmin(fmax(fmax(y1, y2), 0.0), height); + double floor_fymin = floor(fymin); + double ceil_fymax = ceil(fymax); + size_t ymin = (size_t)floor_fymin; + size_t ymax = (size_t)ceil_fymax; + size_t y, cellY; + double rowx1, rowx2; + double rxmin, rxmax, floor_rxmin; + double rymax, rymin, ceil_rxmax; + ssize_t x, xmin, xmax, xmin_cropped, xmax_cropped; + double cxmin, cxmax; + + ymax += (size_t)(ymax == ymin); + for (y = ymin; y < ymax; y++) { + rymin = (double)y; + rymax = (double)y + 1.0; + rymin = fmax(rymin, fymin); + rymax = fmin(rymax, fymax); + rowx1 = dy ? x1 + (rymin - y1) * dx / dy : x1; + rowx2 = dy ? x2 + (rymax - y2) * dx / dy : x2; + rxmin = fmin(rowx1, rowx2); + rxmax = fmax(rowx1, rowx2); + cellY = y * raster->width; + + floor_rxmin = floor(rxmin); + ceil_rxmax = ceil(rxmax); + xmin = (ssize_t)floor_rxmin; + xmax = (ssize_t)ceil_rxmax; + xmax += (ssize_t)(xmax == xmin); + xmin_cropped = xmin; + xmax_cropped = xmax; + if (xmin < 0.0) { + cxmin = (double)xmin; + cxmax = xmax < 0 ? (double)(xmax + 1) : 0.0; + draw_cell(raster, x1, y1, x2, y2, dx, dy, cxmin, cxmax, rxmin, rxmax, rymin, rymax, xmin, cellY); + xmin_cropped = 0; } - - /* Select cell to draw in */ - xfloor = xdir >= 0 ? floor(prevX) : floor(x); - yfloor = ydir >= 0 ? floor(prevY) : floor(y); - cellX = (ssize_t)xfloor; - cellY = (ssize_t)yfloor; - - /* Do not draw if above or below the raster */ - if (cellY < 0 || (size_t)cellY >= raster->height) - continue; - - if (cellX < 0) { - /* Draw on first column in raster of outside of raster on the left side, - * with full horizontal coverage, `.opposite_coverage` one the actual - * cell (outside of the raster) will contribute to the first column's - * `.cell_coverage` */ - cell = (size_t)cellY * raster->width; - raster->cells[cell].opposite_coverage += y - prevY; - raster->cells[cell].cell_coverage += y - prevY; - - } else if ((size_t)cellX >= raster->width) { - /* If outside of the raster, on the right side, add to the last - * columns contributions to the next column, so that the ink level - * balances out to zero */ - cell = (size_t)cellY * raster->width + (raster->width - 1); - raster->cells[cell].opposite_coverage += y - prevY; - - } else { - /* If inside the raster, just draw the line */ - cell = (size_t)cellY * raster->width + (size_t)cellX; - raster->cells[cell].opposite_coverage += y - prevY; - raster->cells[cell].cell_coverage += (y - prevY) * fabs(xfloor + 1.0 - (x + prevX) / 2.0); + if (xmax > iwidth) { + cxmin = fmax(floor_rxmin, width); + cxmax = (double)xmax; + draw_cell(raster, x1, y1, x2, y2, dx, dy, cxmin, cxmax, rxmin, rxmax, rymin, rymax, (ssize_t)cxmin, cellY); + xmax_cropped = iwidth; + } + for (x = xmin_cropped; x < xmax_cropped; x++) { + cxmin = (double)x; + cxmax = (double)x + 1.0; + draw_cell(raster, x1, y1, x2, y2, dx, dy, cxmin, cxmax, rxmin, rxmax, rymin, rymax, x, cellY); } } } |