/* See LICENSE file for copyright and license details. */ #include "common.h" #ifndef TEST void rtgrpblib_draw_linear_bezier(RASTER *restrict raster, double x1, double y1, double x2, double y2) { double w, h, dx, dy, x, y; if (raster->draftness <= 0) { draw_linear_bezier_reference(raster, x1, y1, x2, y2); return; } #define CALC_SLOPE()\ do {\ dx = x2 - x1;\ dy = y2 - y1;\ } while (0) CALC_SLOPE(); if (!dy) { /* For horizontal lines, it is enough that we have the * corners mapped onto the raster, which we must have * since a glyph cannot just contain a line, but outlines: * in `fill`, the line will not make a contribution because * `.opposite_coverage` for each cell will be zero, and * consequentially, `.cell_coverage` will also be zero */ return; } /* We cut of everything above and below the raster, * as these do not contribute to the result */ h = (double)raster->height; if (y1 <= 0 && y2 <= 0) return; if (y1 >= h && y2 >= h) return; if (y1 < 0 && y2 >= 0) { x1 = x1 + (0 - y1) * dx / dy; y1 = 0; CALC_SLOPE(); } else if (y2 < 0 && y1 >= 0) { x2 = x1 + (0 - y1) * dx / dy; y2 = 0; CALC_SLOPE(); } if (y1 <= h && y2 > h) { x2 = x1 + (h - y1) * dx / dy; y2 = h; CALC_SLOPE(); } else if (y2 <= h && y1 > h) { x1 = x1 + (h - y1) * dx / dy; y1 = h; CALC_SLOPE(); } /* Dealing with the left and the right section * is not as simple as `fill` assumes that the * sum of each line's `.opposite_coverage` is 0. * Therefore must must identify the parts of the * line that are left of the raster, right of * the raster, and inside the raster. The parts * that are outside the raster are move to the * edge, or rather their show on the edges are * used. */ w = (double)raster->width; if (x1 < 0 && x2 < 0) { draw_vertical_line(raster, 0, y1, y2, SIGNUM(dy)); return; } if (x1 >= w && x2 >= w) { draw_vertical_line_opposite_only(raster, raster->width - 1, y1, y2, SIGNUM(dy)); return; } if (x1 < 0 && x2 >= 0) { y = y1 + (0 - x1) * dy / dx; x = 0; draw_vertical_line(raster, 0, y1, y, SIGNUM(y - y1)); x1 = x; y1 = y; CALC_SLOPE(); } else if (x2 < 0 && x1 >= 0) { y = y1 + (0 - x1) * dy / dx; x = 0; draw_vertical_line(raster, 0, y, y2, SIGNUM(y2 - y)); x2 = x; y2 = y; CALC_SLOPE(); } if (x1 < w && x2 >= w) { y = y1 + (w - x1) * dy / dx; x = w; draw_vertical_line_opposite_only(raster, raster->width - 1, y, y2, SIGNUM(y2 - y)); x2 = x; y2 = y; CALC_SLOPE(); } else if (x2 < w && x1 >= w) { y = y1 + (w - x1) * dy / dx; x = w; draw_vertical_line_opposite_only(raster, raster->width - 1, y1, y, SIGNUM(y - y1)); x1 = x; y1 = y; CALC_SLOPE(); } /* Now we can finally draw the part of the * line that is inside the raster */ if (!dx) { /* Optimisation for vertical lines. It also serves * to illustrate how the algorithm is designed. */ draw_vertical_line(raster, x1, y1, y2, SIGNUM(dy)); } else { draw_diagonal_line(raster, x1, y1, x2, y2, dx, dy, SIGNUM(dx), SIGNUM(dy)); } #undef CALC_SLOPE } #else static RASTER *raster; static RASTER *refraster; #ifdef ROWWISE_RESET_INKLEVEL static void draw_right_edge_shadow(double x1, double y1, double x2, double y2) { if (raster->draftness <= 0) return; if (x1 >= 10.0 && x2 >= 10.0) { draw_linear_bezier_reference(raster, x1, y1, x2, y2); } else if (x1 >= 10.0) { y2 = y2 + (10.0 - x2) * (y2 - y1) / (x2 - x1); x2 = 10.0; draw_linear_bezier_reference(raster, x1, y1, x2, y2); } else if (x2 >= 10.0) { y1 = y1 + (10.0 - x1) * (y2 - y1) / (x2 - x1); x1 = 10.0; draw_linear_bezier_reference(raster, x1, y1, x2, y2); } } #endif static void proper_check_draw_linear_bezier(double x1, double y1, double x2, double y2) { size_t i; alarm(5); rtgrpblib_draw_linear_bezier(raster, x1, y1, x2, y2); #ifdef ROWWISE_RESET_INKLEVEL draw_right_edge_shadow(x1, y1, x2, y2); #endif alarm(5); draw_linear_bezier_reference(refraster, x1, y1, x2, y2); alarm(0); for (i = 0; i < 100; i++) { ASSERT(eq(raster->cells[i].cell_coverage, refraster->cells[i].cell_coverage)); ASSERT(eq(raster->cells[i].opposite_coverage, refraster->cells[i].opposite_coverage)); } rtgrpblib_reset_raster(raster, 10, 10); rtgrpblib_reset_raster(refraster, 10, 10); } static void check_draw_linear_bezier(double x1, double y1, double x2, double y2) { proper_check_draw_linear_bezier(floor(x1), floor(y1), floor(x2), floor(y2)); proper_check_draw_linear_bezier(floor(x1), y1, floor(x2), y2); proper_check_draw_linear_bezier(x1, floor(y1), x2, floor(y2)); proper_check_draw_linear_bezier(x1, y1, x2, y2); } static double frand(double a, double b) { int i = rand(); double f = (double)i / (double)RAND_MAX; double max = fmax(a, b); double min = fmin(a, b); return fma(f, max - min, min); } int main(void) { double x1, x2; double y1, y2; size_t i; raster = rtgrpblib_create_raster(10, 10); ASSERT(raster); refraster = rtgrpblib_create_raster(10, 10); ASSERT(refraster); srand((unsigned)(uintptr_t)raster); for (i = 0; i < 50000UL; i++) { x1 = frand(-5, 15); y1 = frand(-5, 15); x2 = frand(-5, 15); y2 = frand(-5, 15); check_draw_linear_bezier(x1, y1, x2, y2); check_draw_linear_bezier(x1, x1, x2, x2); check_draw_linear_bezier(x1, y1, x2, y1); check_draw_linear_bezier(x1, x2, x2, x1); check_draw_linear_bezier(x1, y1, x1, y2); check_draw_linear_bezier(x1, x1, x1, x1); raster->draftness = -1; check_draw_linear_bezier(x1, y1, x2, y2); check_draw_linear_bezier(x1, x1, x2, x2); check_draw_linear_bezier(x1, y1, x2, y1); check_draw_linear_bezier(x1, x2, x2, x1); check_draw_linear_bezier(x1, y1, x1, y2); check_draw_linear_bezier(x1, x1, x1, x1); raster->draftness = 0.5; } free(raster); free(refraster); return 0; } #endif