From 038f09783dd0ca499d4ea106836a801d810131ac Mon Sep 17 00:00:00 2001 From: David Thomas Date: Sun, 10 Mar 2024 12:48:25 +0000 Subject: [PATCH] Hoist out line_clip and screen_draw_* --- CMakeLists.txt | 3 + include/framebuf/pixelfmt.h | 5 +- include/geom/line.h | 31 ++ libraries/framebuf/screen/screen-draw.c | 430 ++++++++++++++++++++ libraries/framebuf/screen/screen.c | 508 +----------------------- libraries/geom/line/line.c | 116 ++++++ 6 files changed, 584 insertions(+), 509 deletions(-) create mode 100644 include/geom/line.h create mode 100644 libraries/framebuf/screen/screen-draw.c create mode 100644 libraries/geom/line/line.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8de22ab..8406b7a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,7 @@ set(PUBLIC_HEADERS include/framebuf/span.h include/geom/box.h include/geom/layout.h + include/geom/line.h include/geom/packer.h include/geom/point.h include/io/path.h @@ -225,6 +226,7 @@ set(FRAMEBUF_SOURCES libraries/framebuf/palettes/palettes.c libraries/framebuf/pixelfmt/log2bpp.c libraries/framebuf/screen/screen.c + libraries/framebuf/screen/screen-draw.c libraries/framebuf/span-registry/get.c libraries/framebuf/span-registry/regdata.h libraries/framebuf/span/all8888.c @@ -249,6 +251,7 @@ set(GEOM_SOURCES libraries/geom/box/translated.c libraries/geom/box/union.c libraries/geom/layout/layout.c + libraries/geom/line/line.c libraries/geom/packer/impl.h libraries/geom/packer/packer.c) diff --git a/include/framebuf/pixelfmt.h b/include/framebuf/pixelfmt.h index 02e33b5..e23420e 100644 --- a/include/framebuf/pixelfmt.h +++ b/include/framebuf/pixelfmt.h @@ -92,8 +92,9 @@ typedef unsigned int pixelfmt_argb8888_t; typedef unsigned int pixelfmt_xxxa8888_t; /* any 32bpp alpha */ typedef unsigned int pixelfmt_any_t; /* generic/unspecified pixel */ -typedef unsigned short pixelfmt_any16_t; /* any 16bpp */ -typedef unsigned int pixelfmt_any32_t; /* any 32bpp */ +typedef unsigned char pixelfmt_any8_t; /* any 8bpp pixel */ +typedef unsigned short pixelfmt_any16_t; /* any 16bpp pixel */ +typedef unsigned int pixelfmt_any32_t; /* any 32bpp pixel */ /* ----------------------------------------------------------------------- */ diff --git a/include/geom/line.h b/include/geom/line.h new file mode 100644 index 0000000..ba37030 --- /dev/null +++ b/include/geom/line.h @@ -0,0 +1,31 @@ +/* line.h -- lines */ + +#ifndef GEOM_LINE_H +#define GEOM_LINE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "geom/box.h" + +/// Clips the line (x0,y0)-(x1,y1) by `clip` and returns the clipped points in `x0` and co. +/// +/// - Parameters: +/// - clip: Rectangular clip region. +/// - x0: X coordinate of first point of line (in/out). +/// - y0: Y coordinate of first point of line (in/out). +/// - x1: X coordinate of second point of line (in/out). +/// - y1: Y coordinate of second point of line (in/out). +int line_clip(const box_t *clip, + int *x0, + int *y0, + int *x1, + int *y1); + +#ifdef __cplusplus +} +#endif + +#endif /* GEOM_LINE_H */ diff --git a/libraries/framebuf/screen/screen-draw.c b/libraries/framebuf/screen/screen-draw.c new file mode 100644 index 0000000..6a9a00a --- /dev/null +++ b/libraries/framebuf/screen/screen-draw.c @@ -0,0 +1,430 @@ +/* screen-draw.c */ + +#include +#include +#include +#include +#include +#include + +#include "base/utils.h" +#include "framebuf/span-registry.h" +#include "geom/line.h" +#include "utils/fxp.h" + +#include "framebuf/screen.h" + +void screen_draw_pixel(screen_t *scr, int x, int y, colour_t colour) +{ + box_t clip; + pixelfmt_any_t pxl; + + if (screen_get_clip(scr, &clip) || !box_contains_point(&clip, x, y)) + return; + + pxl = colour_to_pixel(NULL, 0, colour, scr->format); + switch (pixelfmt_log2bpp(scr->format)) + { + case 3: + { + pixelfmt_any8_t *scrp; + + scrp = scr->base; + scrp += y * scr->rowbytes / sizeof(*scrp) + x; + + *scrp = pxl; + } + break; + + case 4: + { + pixelfmt_any16_t *scrp; + + scrp = scr->base; + scrp += y * scr->rowbytes / sizeof(*scrp) + x; + + *scrp = pxl; + } + break; + + case 5: + { + pixelfmt_any32_t *scrp; + + scrp = scr->base; + scrp += y * scr->rowbytes / sizeof(*scrp) + x; + + *scrp = pxl; + } + break; + + default: + assert("Unimplemented pixel format" == NULL); + break; + } +} + +static void screen_blend_pixel(screen_t *scr, + int x, int y, + colour_t colour, int alpha) +{ + box_t clip; + pixelfmt_any_t colpx; + + assert(alpha >= 0); + assert(alpha <= 255); + + if (screen_get_clip(scr, &clip) || !box_contains_point(&clip, x, y)) + return; + + colpx = colour_to_pixel(NULL, 0, colour, scr->format); + switch (pixelfmt_log2bpp(scr->format)) + { + case 5: + { + pixelfmt_any32_t *scrp; + + scrp = scr->base; + scrp += y * scr->rowbytes / sizeof(*scrp) + x; + + scr->span->blendconst(scrp, scrp, &colpx, 1, alpha); + } + break; + + default: + assert("Unimplemented pixel format" == NULL); + break; + } +} + +/* ----------------------------------------------------------------------- */ + +void screen_draw_rect(screen_t *scr, + int x, int y, + int width, int height, + colour_t colour) +{ + box_t clip_box; + box_t rect_box; + box_t draw_box; + int clipped_width, clipped_height; + pixelfmt_any_t fmt; + + if (screen_get_clip(scr, &clip_box)) + return; /* invalid clipped screen */ + + rect_box.x0 = x; + rect_box.y0 = y; + rect_box.x1 = x + width; + rect_box.y1 = y + height; + if (box_intersection(&clip_box, &rect_box, &draw_box)) + return; + + clipped_width = draw_box.x1 - draw_box.x0; + clipped_height = draw_box.y1 - draw_box.y0; + + fmt = colour_to_pixel(NULL, 0, colour, scr->format); + switch (pixelfmt_log2bpp(scr->format)) + { + case 5: + { + pixelfmt_any32_t *scrp; + int w; + + scrp = scr->base; + scrp += draw_box.y0 * scr->rowbytes / sizeof(*scrp) + draw_box.x0; + while (clipped_height--) + { + for (w = clipped_width; w > 0; w--) + *scrp++ = fmt; + scrp += scr->rowbytes / sizeof(*scrp) - clipped_width; + } + } + break; + + default: + assert("Unimplemented pixel format" == NULL); + break; + } +} + +void screen_draw_square(screen_t *scr, int x, int y, int size, colour_t colour) +{ + screen_draw_rect(scr, x, y, size, size, colour); +} + +/* ----------------------------------------------------------------------- */ + +void screen_draw_line(screen_t *scr, + int x0, int y0, int x1, int y1, + colour_t colour) +{ + box_t clip_box; + int dx, dy; + int adx, ady; + int sx, sy; + int error, e2; + + if (screen_get_clip(scr, &clip_box)) + return; /* invalid clipped screen */ + + if (line_clip(&clip_box, &x0, &y0, &x1, &y1) == 0) + return; + + dx = x1 - x0; + adx = abs(dx); + sx = SGN(dx); + + dy = y1 - y0; + ady = -abs(dy); + sy = SGN(dy); + + error = adx + ady; + + for (;;) + { + screen_draw_pixel(scr, x0, y0, colour); + + if (x0 == x1 && y0 == y1) + break; + + e2 = 2 * error; + if (e2 >= ady) + { + if (x0 == x1) { break; } + error += ady; + x0 += sx; + } + if (e2 <= adx) + { + if (y0 == y1) { break; } + error += adx; + y0 += sy; + } + } +} + +void screen_draw_line_wu_fix8(screen_t *scr, + fix8_t x0_f8, fix8_t y0_f8, fix8_t x1_f8, fix8_t y1_f8, + colour_t colour) +{ + box_t clip_box_f8; + fix8_t dx_f8, dy_f8; + int steep_b; // bool + fix16_t grad_f16; + int xend_i; + fix8_t yend_f8; + fix8_t xgap_f8; + int ix0_i, iy0_i; + int alpha1_i, alpha2_i; + fix8_t yf_f8; + int ix1_i, iy1_i; + int x_i, y_i; + + if (screen_get_clip(scr, &clip_box_f8)) + return; /* invalid clipped screen */ + + /* scale up screen clip box to match the coordinate type */ + box_scalelog2(&clip_box_f8, FIX8_SHIFT); + + if (line_clip(&clip_box_f8, &x0_f8, &y0_f8, &x1_f8, &y1_f8) == 0) + return; + + dx_f8 = x1_f8 - x0_f8; + dy_f8 = y1_f8 - y0_f8; + + steep_b = abs(dy_f8) > abs(dx_f8); + if (steep_b) + { + SWAP(x0_f8, y0_f8); + SWAP(x1_f8, y1_f8); + SWAP(dx_f8, dy_f8); + } + + if (x0_f8 > x1_f8) + { + SWAP(x0_f8, x1_f8); + SWAP(y0_f8, y1_f8); + } + + grad_f16 = (dx_f8 == 0) ? FIX16_ONE : FIX16_ONE * dy_f8 / dx_f8; + + /* start point */ + + xend_i = FIX8_ROUND_TO_INT(x0_f8); + yend_f8 = y0_f8 + grad_f16 * (INT_TO_FIX8(xend_i) - x0_f8) / FIX16_ONE; + xgap_f8 = INT_TO_FIX8(xend_i) + FIX8_ONE / 2 - x0_f8; + assert(xgap_f8 >= 0 && xgap_f8 <= FIX8_ONE); + ix0_i = xend_i; + iy0_i = FIX8_FLOOR_TO_INT(yend_f8); + alpha1_i = (255 * (INT_TO_FIX8(iy0_i) + FIX8_ONE - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; + alpha2_i = (255 * -(INT_TO_FIX8(iy0_i) - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; + if (steep_b) + { + screen_blend_pixel(scr, iy0_i, ix0_i, colour, alpha1_i); + screen_blend_pixel(scr, iy0_i + 1, ix0_i, colour, alpha2_i); + } + else + { + screen_blend_pixel(scr, ix0_i, iy0_i, colour, alpha1_i); + screen_blend_pixel(scr, ix0_i, iy0_i + 1, colour, alpha2_i); + } + + yf_f8 = ((yend_f8 << (FIX16_SHIFT - FIX8_SHIFT)) + grad_f16) >> (FIX16_SHIFT - FIX8_SHIFT); + + /* end point */ + + xend_i = FIX8_ROUND_TO_INT(x1_f8); + yend_f8 = y1_f8 + grad_f16 * (INT_TO_FIX8(xend_i) - x1_f8) / FIX16_ONE; + xgap_f8 = x1_f8 + FIX8_ONE / 2 - INT_TO_FIX8(xend_i); + assert(xgap_f8 >= 0 && xgap_f8 < FIX8_ONE); + ix1_i = xend_i; + iy1_i = FIX8_FLOOR_TO_INT(yend_f8); + alpha1_i = (255 * (INT_TO_FIX8(iy1_i) + FIX8_ONE - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; + alpha2_i = (255 * -(INT_TO_FIX8(iy1_i) - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; + if (steep_b) + { + screen_blend_pixel(scr, iy1_i, ix1_i, colour, alpha1_i); + screen_blend_pixel(scr, iy1_i + 1, ix1_i, colour, alpha2_i); + } + else + { + screen_blend_pixel(scr, ix1_i, iy1_i, colour, alpha1_i); + screen_blend_pixel(scr, ix1_i, iy1_i + 1, colour, alpha2_i); + } + + /* mid points */ + + for (x_i = ix0_i + 1; x_i < ix1_i; x_i++) + { + y_i = FIX8_FLOOR_TO_INT(yf_f8); + alpha1_i = (255 * (INT_TO_FIX8(y_i) + FIX8_ONE - yf_f8)) / FIX8_ONE; + alpha2_i = (255 * -(INT_TO_FIX8(y_i) - yf_f8)) / FIX8_ONE; + if (steep_b) + { + screen_blend_pixel(scr, y_i, x_i, colour, alpha1_i); + screen_blend_pixel(scr, y_i + 1, x_i, colour, alpha2_i); + } + else + { + screen_blend_pixel(scr, x_i, y_i, colour, alpha1_i); + screen_blend_pixel(scr, x_i, y_i + 1, colour, alpha2_i); + } + yf_f8 = ((yf_f8 << (FIX16_SHIFT - FIX8_SHIFT)) + grad_f16) >> (FIX16_SHIFT - FIX8_SHIFT); + } +} + +void screen_draw_line_wu_float(screen_t *scr, + float fx0, float fy0, float fx1, float fy1, + colour_t colour) +{ + box_t clip_box; + int x0, y0, x1, y1; + float dx, dy; + int steep; // bool + float grad; + int xend; + float yend; + float xgap; + int ix0, iy0; + int alpha1, alpha2; + float yf; + int ix1, iy1; + int x, y; + + if (screen_get_clip(scr, &clip_box)) + return; /* invalid clipped screen */ + + // this loses the fp precision, so for now just use it to discard lines. + // screen_draw_pixel will clip too. + x0 = fx0; + y0 = fy0; + x1 = fx1; + y1 = fy1; + if (line_clip(&clip_box, &x0, &y0, &x1, &y1) == 0) + return; + + dx = fx1 - fx0; + dy = fy1 - fy0; + + steep = fabsf(dy) > fabsf(dx); + if (steep) + { + SWAP(fx0, fy0); + SWAP(fx1, fy1); + SWAP(dx, dy); + } + + if (fx0 > fx1) + { + SWAP(fx0, fx1); + SWAP(fy0, fy1); + } + + grad = (dx == 0.0f) ? 1.0f : dy / dx; + + /* start point */ + + xend = (int) lroundf(fx0); + yend = fy0 + grad * (xend - fx0); + xgap = xend + 0.5f - fx0; + assert(xgap >= 0.0f && xgap <= 1.0f); + ix0 = xend; + iy0 = floorf(yend); + alpha1 = 255.0f * (iy0 + 1.0f - yend) * xgap; + alpha2 = 255.0f * -(iy0 - yend) * xgap; + if (steep) + { + screen_blend_pixel(scr, iy0, ix0, colour, alpha1); + screen_blend_pixel(scr, iy0 + 1, ix0, colour, alpha2); + } + else + { + screen_blend_pixel(scr, ix0, iy0, colour, alpha1); + screen_blend_pixel(scr, ix0, iy0 + 1, colour, alpha2); + } + + yf = yend + grad; + + /* end point */ + + xend = (int) lroundf(fx1); + yend = fy1 + grad * (xend - fx1); + xgap = fx1 + 0.5f - xend; + assert(xgap >= 0.0f && xgap < 1.0f); + ix1 = xend; + iy1 = floorf(yend); + alpha1 = 255.0f * (iy1 + 1.0f - yend) * xgap; + alpha2 = 255.0f * -(iy1 - yend) * xgap; + if (steep) + { + screen_blend_pixel(scr, iy1, ix1, colour, alpha1); + screen_blend_pixel(scr, iy1 + 1, ix1, colour, alpha2); + } + else + { + screen_blend_pixel(scr, ix1, iy1, colour, alpha1); + screen_blend_pixel(scr, ix1, iy1 + 1, colour, alpha2); + } + + /* mid points */ + + for (x = ix0 + 1; x < ix1; x++) + { + y = floorf(yf); + alpha1 = 255.0f * (y + 1.0f - yf); + alpha2 = 255.0f * -(y - yf); + if (steep) + { + screen_blend_pixel(scr, y, x, colour, alpha1); + screen_blend_pixel(scr, y + 1, x, colour, alpha2); + } + else + { + screen_blend_pixel(scr, x, y, colour, alpha1); + screen_blend_pixel(scr, x, y + 1, colour, alpha2); + } + yf += grad; + } +} + diff --git a/libraries/framebuf/screen/screen.c b/libraries/framebuf/screen/screen.c index b556caf..985ad7c 100644 --- a/libraries/framebuf/screen/screen.c +++ b/libraries/framebuf/screen/screen.c @@ -9,6 +9,7 @@ #include "base/utils.h" #include "framebuf/span-registry.h" +#include "geom/line.h" #include "utils/fxp.h" #include "framebuf/screen.h" @@ -55,510 +56,3 @@ int screen_get_clip(const screen_t *scr, box_t *clip) return box_intersection(clip, &scr->clip, clip); } - -/* ----------------------------------------------------------------------- */ - -void screen_draw_pixel(screen_t *scr, int x, int y, colour_t colour) -{ - box_t clip; - pixelfmt_any_t pxl; - - if (screen_get_clip(scr, &clip) || !box_contains_point(&clip, x, y)) - return; - - pxl = colour_to_pixel(NULL, 0, colour, scr->format); - switch (pixelfmt_log2bpp(scr->format)) - { - case 5: - { - pixelfmt_any32_t *scrp; - - scrp = scr->base; - scrp += y * scr->rowbytes / sizeof(*scrp) + x; - - *scrp = pxl; - } - break; - - default: - assert("Unimplemented pixel format" == NULL); - break; - } -} - -static void screen_blend_pixel(screen_t *scr, - int x, int y, - colour_t colour, int alpha) -{ - box_t clip; - pixelfmt_any_t colpx; - - assert(alpha >= 0); - assert(alpha <= 255); - - if (screen_get_clip(scr, &clip) || !box_contains_point(&clip, x, y)) - return; - - colpx = colour_to_pixel(NULL, 0, colour, scr->format); - switch (scr->format) - { - case pixelfmt_bgrx8888: - { - pixelfmt_bgrx8888_t *scrp; - - scrp = scr->base; - scrp += y * scr->rowbytes / sizeof(*scrp) + x; - - scr->span->blendconst(scrp, scrp, &colpx, 1, alpha); - } - break; - - default: - assert("Unimplemented pixel format" == NULL); - break; - } -} - -/* ----------------------------------------------------------------------- */ - -void screen_draw_rect(screen_t *scr, - int x, int y, - int width, int height, - colour_t colour) -{ - box_t clip_box; - box_t rect_box; - box_t draw_box; - int clipped_width, clipped_height; - pixelfmt_any_t fmt; - - if (screen_get_clip(scr, &clip_box)) - return; /* invalid clipped screen */ - - rect_box.x0 = x; - rect_box.y0 = y; - rect_box.x1 = x + width; - rect_box.y1 = y + height; - if (box_intersection(&clip_box, &rect_box, &draw_box)) - return; - - clipped_width = draw_box.x1 - draw_box.x0; - clipped_height = draw_box.y1 - draw_box.y0; - - fmt = colour_to_pixel(NULL, 0, colour, scr->format); - switch (pixelfmt_log2bpp(scr->format)) - { - case 5: - { - pixelfmt_any32_t *scrp; - int w; - - scrp = scr->base; - scrp += draw_box.y0 * scr->rowbytes / sizeof(*scrp) + draw_box.x0; - while (clipped_height--) - { - for (w = clipped_width; w > 0; w--) - *scrp++ = fmt; - scrp += scr->rowbytes / sizeof(*scrp) - clipped_width; - } - } - break; - - default: - assert("Unimplemented pixel format" == NULL); - break; - } -} - -void screen_draw_square(screen_t *scr, int x, int y, int size, colour_t colour) -{ - screen_draw_rect(scr, x, y, size, size, colour); -} - -/* ----------------------------------------------------------------------- */ - -/* Cohen-Sutherland line clipping algorithm */ - -typedef unsigned int outcode_t; - -#define outcode_INSIDE (0) -#define outcode_LEFT (1) -#define outcode_RIGHT (2) -#define outcode_BOTTOM (4) -#define outcode_TOP (8) - -static INLINE outcode_t compute_outcode(const box_t *clip, int x, int y) -{ - outcode_t code; - - code = outcode_INSIDE; - - if (x < clip->x0) - code |= outcode_LEFT; - else if (x >= clip->x1) - code |= outcode_RIGHT; - - if (y < clip->y0) - code |= outcode_BOTTOM; - else if (y >= clip->y1) - code |= outcode_TOP; - - return code; -} - -// not necessarily a screen function -static int screen_clip_line(const box_t *clip, - int *px0, - int *py0, - int *px1, - int *py1) -{ - int x0, y0, x1, y1; - outcode_t oc0, oc1; - outcode_t oc; - int w, h; - int x, y; - - x0 = *px0; - y0 = *py0; - x1 = *px1; - y1 = *py1; - - oc0 = compute_outcode(clip, x0, y0); - oc1 = compute_outcode(clip, x1, y1); - - for (;;) - { - if ((oc0 | oc1) == outcode_INSIDE) - { - /* both points lie inside clip - draw */ - - *px0 = x0; - *py0 = y0; - *px1 = x1; - *py1 = y1; - - return 1; - } - else if ((oc0 & oc1) != 0) - { - /* both points lie outside clip - don't draw */ - return 0; - } - else - { - oc = oc1 > oc0 ? oc1 : oc0; - w = x1 - x0; - h = y1 - y0; - - if (oc & outcode_TOP) - { - x = x0 + w * (clip->y1 - 1 - y0) / h; - y = clip->y1 - 1; - } - else if (oc & outcode_BOTTOM) - { - x = x0 + w * (clip->y0 - y0) / h; - y = clip->y0; - } - else if (oc & outcode_RIGHT) - { - x = clip->x1 - 1; - y = y0 + h * (clip->x1 - 1 - x0) / w; - } - else if (oc & outcode_LEFT) - { - x = clip->x0; - y = y0 + h * (clip->x0 - x0) / w; - } - - if (oc == oc0) - { - x0 = x; - y0 = y; - oc0 = compute_outcode(clip, x0, y0); - } - else - { - x1 = x; - y1 = y; - oc1 = compute_outcode(clip, x1, y1); - } - } - } -} - -/* ----------------------------------------------------------------------- */ - -void screen_draw_line(screen_t *scr, - int x0, int y0, int x1, int y1, - colour_t colour) -{ - box_t clip_box; - int dx, dy; - int adx, ady; - int sx, sy; - int error, e2; - - if (screen_get_clip(scr, &clip_box)) - return; /* invalid clipped screen */ - - if (screen_clip_line(&clip_box, &x0, &y0, &x1, &y1) == 0) - return; - - dx = x1 - x0; - adx = abs(dx); - sx = SGN(dx); - - dy = y1 - y0; - ady = -abs(dy); - sy = SGN(dy); - - error = adx + ady; - - for (;;) - { - screen_draw_pixel(scr, x0, y0, colour); - - if (x0 == x1 && y0 == y1) - break; - - e2 = 2 * error; - if (e2 >= ady) - { - if (x0 == x1) { break; } - error += ady; - x0 += sx; - } - if (e2 <= adx) - { - if (y0 == y1) { break; } - error += adx; - y0 += sy; - } - } -} - -void screen_draw_line_wu_fix8(screen_t *scr, - fix8_t x0_f8, fix8_t y0_f8, fix8_t x1_f8, fix8_t y1_f8, - colour_t colour) -{ - box_t clip_box_f8; - fix8_t dx_f8, dy_f8; - int steep_b; // bool - fix16_t grad_f16; - int xend_i; - fix8_t yend_f8; - fix8_t xgap_f8; - int ix0_i, iy0_i; - int alpha1_i, alpha2_i; - fix8_t yf_f8; - int ix1_i, iy1_i; - int x_i, y_i; - - if (screen_get_clip(scr, &clip_box_f8)) - return; /* invalid clipped screen */ - - /* scale up screen clip box to match the coordinate type */ - box_scalelog2(&clip_box_f8, FIX8_SHIFT); - - if (screen_clip_line(&clip_box_f8, &x0_f8, &y0_f8, &x1_f8, &y1_f8) == 0) - return; - - dx_f8 = x1_f8 - x0_f8; - dy_f8 = y1_f8 - y0_f8; - - steep_b = abs(dy_f8) > abs(dx_f8); - if (steep_b) - { - SWAP(x0_f8, y0_f8); - SWAP(x1_f8, y1_f8); - SWAP(dx_f8, dy_f8); - } - - if (x0_f8 > x1_f8) - { - SWAP(x0_f8, x1_f8); - SWAP(y0_f8, y1_f8); - } - - grad_f16 = (dx_f8 == 0) ? FIX16_ONE : FIX16_ONE * dy_f8 / dx_f8; - - /* start point */ - - xend_i = FIX8_ROUND_TO_INT(x0_f8); - yend_f8 = y0_f8 + grad_f16 * (INT_TO_FIX8(xend_i) - x0_f8) / FIX16_ONE; - xgap_f8 = INT_TO_FIX8(xend_i) + FIX8_ONE / 2 - x0_f8; - assert(xgap_f8 >= 0 && xgap_f8 <= FIX8_ONE); - ix0_i = xend_i; - iy0_i = FIX8_FLOOR_TO_INT(yend_f8); - alpha1_i = (255 * (INT_TO_FIX8(iy0_i) + FIX8_ONE - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; - alpha2_i = (255 * -(INT_TO_FIX8(iy0_i) - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; - if (steep_b) - { - screen_blend_pixel(scr, iy0_i, ix0_i, colour, alpha1_i); - screen_blend_pixel(scr, iy0_i + 1, ix0_i, colour, alpha2_i); - } - else - { - screen_blend_pixel(scr, ix0_i, iy0_i, colour, alpha1_i); - screen_blend_pixel(scr, ix0_i, iy0_i + 1, colour, alpha2_i); - } - - yf_f8 = ((yend_f8 << (FIX16_SHIFT - FIX8_SHIFT)) + grad_f16) >> (FIX16_SHIFT - FIX8_SHIFT); - - /* end point */ - - xend_i = FIX8_ROUND_TO_INT(x1_f8); - yend_f8 = y1_f8 + grad_f16 * (INT_TO_FIX8(xend_i) - x1_f8) / FIX16_ONE; - xgap_f8 = x1_f8 + FIX8_ONE / 2 - INT_TO_FIX8(xend_i); - assert(xgap_f8 >= 0 && xgap_f8 < FIX8_ONE); - ix1_i = xend_i; - iy1_i = FIX8_FLOOR_TO_INT(yend_f8); - alpha1_i = (255 * (INT_TO_FIX8(iy1_i) + FIX8_ONE - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; - alpha2_i = (255 * -(INT_TO_FIX8(iy1_i) - yend_f8) * xgap_f8 / FIX8_ONE) / FIX8_ONE; - if (steep_b) - { - screen_blend_pixel(scr, iy1_i, ix1_i, colour, alpha1_i); - screen_blend_pixel(scr, iy1_i + 1, ix1_i, colour, alpha2_i); - } - else - { - screen_blend_pixel(scr, ix1_i, iy1_i, colour, alpha1_i); - screen_blend_pixel(scr, ix1_i, iy1_i + 1, colour, alpha2_i); - } - - /* mid points */ - - for (x_i = ix0_i + 1; x_i < ix1_i; x_i++) - { - y_i = FIX8_FLOOR_TO_INT(yf_f8); - alpha1_i = (255 * (INT_TO_FIX8(y_i) + FIX8_ONE - yf_f8)) / FIX8_ONE; - alpha2_i = (255 * -(INT_TO_FIX8(y_i) - yf_f8)) / FIX8_ONE; - if (steep_b) - { - screen_blend_pixel(scr, y_i, x_i, colour, alpha1_i); - screen_blend_pixel(scr, y_i + 1, x_i, colour, alpha2_i); - } - else - { - screen_blend_pixel(scr, x_i, y_i, colour, alpha1_i); - screen_blend_pixel(scr, x_i, y_i + 1, colour, alpha2_i); - } - yf_f8 = ((yf_f8 << (FIX16_SHIFT - FIX8_SHIFT)) + grad_f16) >> (FIX16_SHIFT - FIX8_SHIFT); - } -} - -void screen_draw_line_wu_float(screen_t *scr, - float fx0, float fy0, float fx1, float fy1, - colour_t colour) -{ - box_t clip_box; - int x0, y0, x1, y1; - float dx, dy; - int steep; // bool - float grad; - int xend; - float yend; - float xgap; - int ix0, iy0; - int alpha1, alpha2; - float yf; - int ix1, iy1; - int x, y; - - if (screen_get_clip(scr, &clip_box)) - return; /* invalid clipped screen */ - - // this loses the fp precision, so for now just use it to discard lines. - // screen_draw_pixel will clip too. - x0 = fx0; - y0 = fy0; - x1 = fx1; - y1 = fy1; - if (screen_clip_line(&clip_box, &x0, &y0, &x1, &y1) == 0) - return; - - dx = fx1 - fx0; - dy = fy1 - fy0; - - steep = fabsf(dy) > fabsf(dx); - if (steep) - { - SWAP(fx0, fy0); - SWAP(fx1, fy1); - SWAP(dx, dy); - } - - if (fx0 > fx1) - { - SWAP(fx0, fx1); - SWAP(fy0, fy1); - } - - grad = (dx == 0.0f) ? 1.0f : dy / dx; - - /* start point */ - - xend = (int) lroundf(fx0); - yend = fy0 + grad * (xend - fx0); - xgap = xend + 0.5f - fx0; - assert(xgap >= 0.0f && xgap <= 1.0f); - ix0 = xend; - iy0 = floorf(yend); - alpha1 = 255.0f * (iy0 + 1.0f - yend) * xgap; - alpha2 = 255.0f * -(iy0 - yend) * xgap; - if (steep) - { - screen_blend_pixel(scr, iy0, ix0, colour, alpha1); - screen_blend_pixel(scr, iy0 + 1, ix0, colour, alpha2); - } - else - { - screen_blend_pixel(scr, ix0, iy0, colour, alpha1); - screen_blend_pixel(scr, ix0, iy0 + 1, colour, alpha2); - } - - yf = yend + grad; - - /* end point */ - - xend = (int) lroundf(fx1); - yend = fy1 + grad * (xend - fx1); - xgap = fx1 + 0.5f - xend; - assert(xgap >= 0.0f && xgap < 1.0f); - ix1 = xend; - iy1 = floorf(yend); - alpha1 = 255.0f * (iy1 + 1.0f - yend) * xgap; - alpha2 = 255.0f * -(iy1 - yend) * xgap; - if (steep) - { - screen_blend_pixel(scr, iy1, ix1, colour, alpha1); - screen_blend_pixel(scr, iy1 + 1, ix1, colour, alpha2); - } - else - { - screen_blend_pixel(scr, ix1, iy1, colour, alpha1); - screen_blend_pixel(scr, ix1, iy1 + 1, colour, alpha2); - } - - /* mid points */ - - for (x = ix0 + 1; x < ix1; x++) - { - y = floorf(yf); - alpha1 = 255.0f * (y + 1.0f - yf); - alpha2 = 255.0f * -(y - yf); - if (steep) - { - screen_blend_pixel(scr, y, x, colour, alpha1); - screen_blend_pixel(scr, y + 1, x, colour, alpha2); - } - else - { - screen_blend_pixel(scr, x, y, colour, alpha1); - screen_blend_pixel(scr, x, y + 1, colour, alpha2); - } - yf += grad; - } -} diff --git a/libraries/geom/line/line.c b/libraries/geom/line/line.c new file mode 100644 index 0000000..4ee3a1c --- /dev/null +++ b/libraries/geom/line/line.c @@ -0,0 +1,116 @@ +/* line.c -- Cohen-Sutherland line clipping algorithm */ + +#include "base/utils.h" +#include "geom/box.h" + +#include "geom/line.h" + +typedef unsigned int outcode_t; + +#define outcode_INSIDE (0) +#define outcode_LEFT (1u << 0) +#define outcode_RIGHT (1u << 1) +#define outcode_BOTTOM (1u << 2) +#define outcode_TOP (1u << 3) + +static INLINE outcode_t compute_outcode(const box_t *clip, int x, int y) +{ + outcode_t code; + + code = outcode_INSIDE; + + if (x < clip->x0) + code |= outcode_LEFT; + else if (x >= clip->x1) + code |= outcode_RIGHT; + + if (y < clip->y0) + code |= outcode_BOTTOM; + else if (y >= clip->y1) + code |= outcode_TOP; + + return code; +} + +int line_clip(const box_t *clip, + int *px0, + int *py0, + int *px1, + int *py1) +{ + int x0, y0, x1, y1; + outcode_t oc0, oc1; + outcode_t oc; + int w, h; + int x, y; + + x0 = *px0; + y0 = *py0; + x1 = *px1; + y1 = *py1; + + oc0 = compute_outcode(clip, x0, y0); + oc1 = compute_outcode(clip, x1, y1); + + /* loop until either both points are inside the clip region (in which case + * draw) or both points are outside the clip (in which case don't). */ + for (;;) + { + if ((oc0 | oc1) == outcode_INSIDE) + { + /* both points lie inside clip - draw */ + + *px0 = x0; + *py0 = y0; + *px1 = x1; + *py1 = y1; + + return 1; + } + else if ((oc0 & oc1) != 0) + { + /* both points lie outside clip - don't draw */ + return 0; + } + else + { + oc = oc1 > oc0 ? oc1 : oc0; + w = x1 - x0; + h = y1 - y0; + + if (oc & outcode_TOP) + { + x = x0 + w * (clip->y1 - 1 - y0) / h; + y = clip->y1 - 1; + } + else if (oc & outcode_BOTTOM) + { + x = x0 + w * (clip->y0 - y0) / h; + y = clip->y0; + } + else if (oc & outcode_RIGHT) + { + x = clip->x1 - 1; + y = y0 + h * (clip->x1 - 1 - x0) / w; + } + else if (oc & outcode_LEFT) + { + x = clip->x0; + y = y0 + h * (clip->x0 - x0) / w; + } + + if (oc == oc0) + { + x0 = x; + y0 = y; + oc0 = compute_outcode(clip, x0, y0); + } + else + { + x1 = x; + y1 = y; + oc1 = compute_outcode(clip, x1, y1); + } + } + } +}