diff options
author | Vincent Sanders <vince@netsurf-browser.org> | 2010-01-20 17:18:28 +0000 |
---|---|---|
committer | Vincent Sanders <vince@netsurf-browser.org> | 2010-01-20 17:18:28 +0000 |
commit | 131b6c4a00575c9e996a9ae60c90a9647fb5ef75 (patch) | |
tree | dba439673b801a22a833270dbd615628bf0614e4 /src/plot/generic.c | |
parent | a3097232844a20ea918d63722dbe6a7c71493bab (diff) | |
download | libnsfb-131b6c4a00575c9e996a9ae60c90a9647fb5ef75.tar.gz libnsfb-131b6c4a00575c9e996a9ae60c90a9647fb5ef75.tar.bz2 |
move plot functions to their own sub directory
fix 8 and 16bpp plotters when used with cursor
svn path=/trunk/libnsfb/; revision=9850
Diffstat (limited to 'src/plot/generic.c')
-rw-r--r-- | src/plot/generic.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/src/plot/generic.c b/src/plot/generic.c new file mode 100644 index 0000000..d04559e --- /dev/null +++ b/src/plot/generic.c @@ -0,0 +1,663 @@ +/* + * Copyright 2009 Vincent Sanders <vince@simtec.co.uk> + * Copyright 2009 Michael Drake <tlsa@netsurf-browser.org> + * + * This file is part of libnsfb, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** \file + * generic plotter functions which are not depth dependant (implementation). + */ + +#include <stdbool.h> +#include <limits.h> +#include <malloc.h> +#include <string.h> + +#include "libnsfb.h" +#include "libnsfb_plot.h" +#include "libnsfb_plot_util.h" + +#include "nsfb.h" +#include "plot.h" +#include "frontend.h" + +extern const nsfb_plotter_fns_t _nsfb_1bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_8bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_16bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_24bpp_plotters; +extern const nsfb_plotter_fns_t _nsfb_32bpp_plotters; + +static bool set_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) +{ + nsfb_bbox_t fbarea; + + /* screen area */ + fbarea.x0 = 0; + fbarea.y0 = 0; + fbarea.x1 = nsfb->width; + fbarea.y1 = nsfb->height; + + if (clip == NULL) { + nsfb->clip = fbarea; + } else { + if (!nsfb_plot_clip(&fbarea, clip)) + return false; + + nsfb->clip = *clip; + } + return true; +} + +static bool get_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) +{ + *clip = nsfb->clip; + return true; +} + +static bool clg(nsfb_t *nsfb, nsfb_colour_t c) +{ + return nsfb->plotter_fns->fill(nsfb, &nsfb->clip, c); +} + +/** + * Find first filled span along horizontal line at given coordinate + * + * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) + * \param n number of polygon vertex values (N * 2) + * \param x current position along current scan line + * \param y position of current scan line + * \param x0 updated to start of filled area + * \param x1 updated to end of filled area + * \return true if an intersection was found + */ +static bool find_span(const int *p, int n, int x, int y, int *x0, int *x1) +{ + int i; + int p_x0, p_y0; + int p_x1, p_y1; + int x_new; + bool direction = false; + + *x0 = *x1 = INT_MAX; + + for (i = 0; i < n; i = i + 2) { + /* get line endpoints */ + if (i != n - 2) { + /* not the last line */ + p_x0 = p[i]; p_y0 = p[i + 1]; + p_x1 = p[i + 2]; p_y1 = p[i + 3]; + } else { + /* last line; 2nd endpoint is first vertex */ + p_x0 = p[i]; p_y0 = p[i + 1]; + p_x1 = p[0]; p_y1 = p[1]; + } + /* ignore horizontal lines */ + if (p_y0 == p_y1) + continue; + + /* ignore lines that don't cross this y level */ + if ((y < p_y0 && y < p_y1) || (y > p_y0 && y > p_y1)) + continue; + + if (p_x0 == p_x1) { + /* vertical line, x is constant */ + x_new = p_x0; + } else { + /* find intersect */ + x_new = p_x0 + ((long long)(y - p_y0) * (p_x1 - p_x0)) / + (p_y1 - p_y0); + } + + /* ignore intersections before current x */ + if (x_new < x) + continue; + + /* set nearest intersections as filled area endpoints */ + if (x_new < *x0) { + /* nearer than first endpoint */ + *x1 = *x0; + *x0 = x_new; + direction = (p_y0 > p_y1); + } else if (x_new == *x0) { + /* same as first endpoint */ + if ((p_y0 > p_y1) != direction) + *x1 = x_new; + } else if (x_new < *x1) { + /* nearer than second endpoint */ + *x1 = x_new; + } + + } + if (*x0 == INT_MAX) + /* no span found */ + return false; + + /* span found */ + if (*x1 == INT_MAX) { + *x1 = *x0; + *x0 = x; + return true; + } + + return true; +} + + +/** + * Plot a polygon + * + * \param nsfb framebuffer context + * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) + * \param n number of polygon vertices (N) + * \param c fill colour + * \return true if no errors + */ +static bool +polygon(nsfb_t *nsfb, const int *p, unsigned int n, nsfb_colour_t c) +{ + int poly_x0, poly_y0; /* Bounding box top left corner */ + int poly_x1, poly_y1; /* Bounding box bottom right corner */ + int i, j; /* indexes */ + int x0, x1; /* filled span extents */ + int y; /* current y coordinate */ + int y_max; /* bottom of plot area */ + nsfb_bbox_t fline; + nsfb_plot_pen_t pen; + + /* find no. of vertex values */ + int v = n * 2; + + /* Can't plot polygons with 2 or fewer vertices */ + if (n <= 2) + return true; + + pen.stroke_colour = c; + + /* Find polygon bounding box */ + poly_x0 = poly_x1 = *p; + poly_y0 = poly_y1 = p[1]; + for (i = 2; i < v; i = i + 2) { + j = i + 1; + if (p[i] < poly_x0) + poly_x0 = p[i]; + else if (p[i] > poly_x1) + poly_x1 = p[i]; + if (p[j] < poly_y0) + poly_y0 = p[j]; + else if (p[j] > poly_y1) + poly_y1 = p[j]; + } + + /* Don't try to plot it if it's outside the clip rectangle */ + if (nsfb->clip.y1 < poly_y0 || + nsfb->clip.y0 > poly_y1 || + nsfb->clip.x1 < poly_x0 || + nsfb->clip.x0 > poly_x1) + return true; + + /* Find the top of the important area */ + if (poly_y0 > nsfb->clip.y0) + y = poly_y0; + else + y = nsfb->clip.y0; + + /* Find the bottom of the important area */ + if (poly_y1 < nsfb->clip.y1) + y_max = poly_y1; + else + y_max = nsfb->clip.y1; + + for (; y < y_max; y++) { + x1 = poly_x0; + /* For each row */ + while (find_span(p, v, x1, y, &x0, &x1)) { + /* don't draw anything outside clip region */ + if (x1 < nsfb->clip.x0) + continue; + else if (x0 < nsfb->clip.x0) + x0 = nsfb->clip.x0; + if (x0 > nsfb->clip.x1) + break; + else if (x1 > nsfb->clip.x1) + x1 = nsfb->clip.x1; + + fline.x0 = x0; + fline.y0 = y; + fline.x1 = x1; + fline.y1 = y; + + /* draw this filled span on current row */ + nsfb->plotter_fns->line(nsfb, 1, &fline, &pen); + + /* don't look for more spans if already at end of clip + * region or polygon */ + if (x1 == nsfb->clip.x1 || x1 == poly_x1) + break; + + if (x0 == x1) + x1++; + } + } + return true; +} + +static bool +rectangle(nsfb_t *nsfb, nsfb_bbox_t *rect, + int line_width, nsfb_colour_t c, + bool dotted, bool dashed) +{ + nsfb_bbox_t side[4]; + nsfb_plot_pen_t pen; + + pen.stroke_colour = c; + pen.stroke_width = line_width; + if (dotted || dashed) { + pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN; + } else { + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + } + + side[0] = *rect; + side[1] = *rect; + side[2] = *rect; + side[3] = *rect; + + side[0].y1 = side[0].y0; + side[1].y0 = side[1].y1; + side[2].x1 = side[2].x0; + side[3].x0 = side[3].x1; + + return nsfb->plotter_fns->line(nsfb, 4, side, &pen); +} + +/* plotter routine for ellipse points */ +static void +ellipsepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); +} + +static void +ellipsefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb_bbox_t fline[2]; + nsfb_plot_pen_t pen; + + pen.stroke_colour = c; + + fline[0].x0 = fline[1].x0 = cx - x; + fline[0].x1 = fline[1].x1 = cx + x; + fline[0].y0 = fline[0].y1 = cy + y; + fline[1].y0 = fline[1].y1 = cy - y; + + nsfb->plotter_fns->line(nsfb, 2, fline, &pen); + +} + +#define ROUND(a) ((int)(a+0.5)) + +static bool +ellipse_midpoint(nsfb_t *nsfb, + int cx, + int cy, + int rx, + int ry, + nsfb_colour_t c, + void (ellipsefn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) +{ + int rx2 = rx * rx; + int ry2 = ry * ry; + int tworx2 = 2 * rx2; + int twory2 = 2 * ry2; + int p; + int x = 0; + int y = ry; + int px = 0; + int py = tworx2 * y; + + ellipsefn(nsfb, cx, cy, x, y, c); + + /* region 1 */ + p = ROUND(ry2 - (rx2 * ry) + (0.25 * rx2)); + while (px < py) { + x++; + px += twory2; + if (p <0) { + p+=ry2 + px; + } else { + y--; + py -= tworx2; + p+=ry2 + px - py; + } + ellipsefn(nsfb, cx, cy, x, y, c); + } + + /* region 2 */ + p = ROUND(ry2*(x+0.5)*(x+0.5) + rx2*(y-1)*(y-1) - rx2*ry2); + while (y > 0) { + y--; + py -= tworx2; + if (p > 0) { + p+=rx2 - py; + } else { + x++; + px += twory2; + p+=rx2 - py + px; + } + ellipsefn(nsfb, cx, cy, x, y, c); + } + return true; +} + + +/* plotter routine for 8way circle symetry */ +static void +circlepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); + nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); + nsfb->plotter_fns->point(nsfb, cx + y, cy + x, c); + nsfb->plotter_fns->point(nsfb, cx - y, cy + x, c); + nsfb->plotter_fns->point(nsfb, cx + y, cy - x, c); + nsfb->plotter_fns->point(nsfb, cx - y, cy - x, c); +} + +static void +circlefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) +{ + nsfb_bbox_t fline[4]; + nsfb_plot_pen_t pen; + + pen.stroke_colour = c; + + fline[0].x0 = fline[1].x0 = cx - x; + fline[0].x1 = fline[1].x1 = cx + x; + fline[0].y0 = fline[0].y1 = cy + y; + fline[1].y0 = fline[1].y1 = cy - y; + + fline[2].x0 = fline[3].x0 = cx - y; + fline[2].x1 = fline[3].x1 = cx + y; + fline[2].y0 = fline[2].y1 = cy + x; + fline[3].y0 = fline[3].y1 = cy - x; + + nsfb->plotter_fns->line(nsfb, 4, fline, &pen); +} + +static bool circle_midpoint(nsfb_t *nsfb, + int cx, + int cy, + int r, + nsfb_colour_t c, + void (circfn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) +{ + int x = 0; + int y = r; + int p = 1 - r; + + circfn(nsfb, cx, cy, x, y, c); + while (x < y) { + x++; + if (p < 0) { + p += 2 * x + 1; + } else { + y--; + p += 2 * (x - y) + 1; + } + circfn(nsfb, cx, cy, x, y, c); + } + return true; +} + +static bool ellipse(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + int width = (ellipse->x1 - ellipse->x0)>>1; + int height = (ellipse->y1 - ellipse->y0)>>1; + + if (width == height) { + /* circle */ + return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlepoints); + } else { + return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsepoints); + } +} + +static bool ellipse_fill(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) +{ + int width = (ellipse->x1 - ellipse->x0) >> 1; + int height = (ellipse->y1 - ellipse->y0) >> 1; + + if (width == height) { + /* circle */ + return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlefill); + } else { + return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsefill); + } +} + + + +/* copy an area of screen from one location to another. + * + * @warning This implementation is woefully incomplete! + */ +static bool +copy(nsfb_t *nsfb, nsfb_bbox_t *srcbox, nsfb_bbox_t *dstbox) +{ + int srcx = srcbox->x0; + int srcy = srcbox->y0; + int dstx = dstbox->x0; + int dsty = dstbox->y0; + int width = dstbox->x1 - dstbox->x0; + int height = dstbox->y1 - dstbox->y0; + uint8_t *srcptr; + uint8_t *dstptr; + int hloop; + nsfb_bbox_t allbox; + + nsfb_plot_add_rect(srcbox, dstbox, &allbox); + + nsfb->frontend_rtns->claim(nsfb, &allbox); + + srcptr = (nsfb->ptr + + (srcy * nsfb->linelen) + + ((srcx * nsfb->bpp) / 8)); + + dstptr = (nsfb->ptr + + (dsty * nsfb->linelen) + + ((dstx * nsfb->bpp) / 8)); + + + if (width == nsfb->width) { + /* take shortcut and use memmove */ + memmove(dstptr, srcptr, (width * height * nsfb->bpp) / 8); + } else { + if (srcy > dsty) { + for (hloop = height; hloop > 0; hloop--) { + memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); + srcptr += nsfb->linelen; + dstptr += nsfb->linelen; + } + } else { + srcptr += height * nsfb->linelen; + dstptr += height * nsfb->linelen; + for (hloop = height; hloop > 0; hloop--) { + srcptr -= nsfb->linelen; + dstptr -= nsfb->linelen; + memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); + } + } + } + + nsfb->frontend_rtns->update(nsfb, dstbox); + + return true; +} + +static bool arc(nsfb_t *nsfb, int x, int y, int radius, int angle1, int angle2, nsfb_colour_t c) +{ + nsfb=nsfb; + x = x; + y = y; + radius = radius; + c = c; + angle1=angle1; + angle2=angle2; + return true; +} + +#define N_SEG 30 + +static bool +cubic(nsfb_t *nsfb, nsfb_bbox_t *curve, nsfb_point_t *ctrla, nsfb_point_t *ctrlb, nsfb_colour_t cl) +{ + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + unsigned int seg_loop; + double t; + double one_minus_t; + double a; + double b; + double c; + double d; + double x; + double y; + + pen.stroke_colour = cl; + + x = curve->x0; + y = curve->y0; + + for (seg_loop = 1; seg_loop <= N_SEG; ++seg_loop) { + t = (double)seg_loop / (double)N_SEG; + + one_minus_t = 1.0 - t; + + a = one_minus_t * one_minus_t * one_minus_t; + b = 3.0 * t * one_minus_t * one_minus_t; + c = 3.0 * t * t * one_minus_t; + d = t * t * t; + + line.x0 = x; + line.y0 = y; + + x = a * curve->x0 + b * ctrla->x + c * ctrlb->x + d * curve->x1; + y = a * curve->y0 + b * ctrla->y + c * ctrlb->y + d * curve->y1; + + line.x1 = x; + line.y1 = y; + + nsfb->plotter_fns->line(nsfb, 1, &line, &pen); + } + + return true; +} + +static bool quadratic(nsfb_t *nsfb, nsfb_bbox_t *curve, nsfb_point_t *ctrla, nsfb_colour_t cl) +{ + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + unsigned int seg_loop; + double t; + double one_minus_t; + double a; + double b; + double c; + double x; + double y; + + pen.stroke_colour = cl; + + x = curve->x0; + y = curve->y0; + + for (seg_loop = 1; seg_loop <= N_SEG; ++seg_loop) { + t = (double)seg_loop / (double)N_SEG; + + one_minus_t = 1.0 - t; + + a = one_minus_t * one_minus_t; + b = 2.0 * t * one_minus_t; + c = t * t; + + line.x0 = x; + line.y0 = y; + + x = a * curve->x0 + b * ctrla->x + c * curve->x1; + y = a * curve->y0 + b * ctrla->y + c * curve->y1; + + line.x1 = x; + line.y1 = y; + + nsfb->plotter_fns->line(nsfb, 1, &line, &pen); + } + + return true; +} + +bool select_plotters(nsfb_t *nsfb) +{ + const nsfb_plotter_fns_t *table = NULL; + + switch (nsfb->bpp) { + /* case 1: + table = &_nsfb_1bpp_plotters; + break; + */ + case 8: + table = &_nsfb_8bpp_plotters; + break; + + case 16: + table = &_nsfb_16bpp_plotters; + break; + + /* + case 24: + table = &_nsfb_24bpp_plotters; + break; + */ + case 32: + table = &_nsfb_32bpp_plotters; + break; + + default: + return false; + } + + if (nsfb->plotter_fns != NULL) + free(nsfb->plotter_fns); + + nsfb->plotter_fns = calloc(1, sizeof(nsfb_plotter_fns_t)); + memcpy(nsfb->plotter_fns, table, sizeof(nsfb_plotter_fns_t)); + + /* set the generics */ + nsfb->plotter_fns->clg = clg; + nsfb->plotter_fns->set_clip = set_clip; + nsfb->plotter_fns->get_clip = get_clip; + nsfb->plotter_fns->polygon = polygon; + nsfb->plotter_fns->rectangle = rectangle; + nsfb->plotter_fns->ellipse = ellipse; + nsfb->plotter_fns->ellipse_fill = ellipse_fill; + nsfb->plotter_fns->copy = copy; + nsfb->plotter_fns->arc = arc; + nsfb->plotter_fns->quadratic = quadratic; + nsfb->plotter_fns->cubic = cubic; + + /* set default clip rectangle to size of framebuffer */ + nsfb->clip.x0 = 0; + nsfb->clip.y0 = 0; + nsfb->clip.x1 = nsfb->width; + nsfb->clip.y1 = nsfb->height; + + return true; +} |