aboutsummaryrefslogtreecommitdiffstats
path: root/draw_linear_bezier_reference.c
diff options
context:
space:
mode:
Diffstat (limited to 'draw_linear_bezier_reference.c')
-rw-r--r--draw_linear_bezier_reference.c156
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);
}
}
}