summaryrefslogtreecommitdiffstats
path: root/src/libnr/nr-svp-render.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/libnr/nr-svp-render.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/libnr/nr-svp-render.cpp')
-rw-r--r--src/libnr/nr-svp-render.cpp615
1 files changed, 615 insertions, 0 deletions
diff --git a/src/libnr/nr-svp-render.cpp b/src/libnr/nr-svp-render.cpp
new file mode 100644
index 000000000..0c3b391d5
--- /dev/null
+++ b/src/libnr/nr-svp-render.cpp
@@ -0,0 +1,615 @@
+#define __NR_SVP_RENDER_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#define NR_SVPSEG_Y0(s,i) ((s)->points[(s)->segments[i].start].y)
+#define NR_SVPSEG_Y1(s,i) ((s)->points[(s)->segments[i].start + (s)->segments[i].length - 1].y)
+
+#define noNR_VERBOSE
+
+#include "nr-svp-render.h"
+
+static void nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int x0, int y0, int x1, int y1,
+ void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data);
+
+static void nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data);
+
+/* Renders graymask of svl into buffer */
+
+void
+nr_pixblock_render_svp_mask_or (NRPixBlock *d, NRSVP *svp)
+{
+ nr_svp_render (svp, NR_PIXBLOCK_PX (d), 1, d->rs,
+ d->area.x0, d->area.y0, d->area.x1, d->area.y1,
+ nr_svp_run_A8_OR, NULL);
+}
+
+static void
+nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data)
+{
+ if ((c0_24 >= 0xff0000) && (s0_24 == 0x0)) {
+ /* Simple copy */
+ while (len > 0) {
+ d[0] = 255;
+ d += 1;
+ len -= 1;
+ }
+ } else {
+ while (len > 0) {
+ unsigned int ca, da;
+ /* Draw */
+ ca = c0_24 >> 16;
+ da = 65025 - (255 - ca) * (255 - d[0]);
+ d[0] = (da + 127) / 255;
+ d += 1;
+ c0_24 += s0_24;
+ c0_24 = CLAMP (c0_24, 0, 16777216);
+ len -= 1;
+ }
+ }
+}
+
+
+struct NRRun {
+ NRRun *next;
+ NR::Coord x0, y0, x1, y1;
+ float step;
+ float final;
+ NR::Coord x;
+ NR::Coord value;
+};
+
+static NRRun *nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind);
+static NRRun *nr_run_free_one (NRRun *run);
+static void nr_run_free_list (NRRun *run);
+static NRRun *nr_run_insort (NRRun *start, NRRun *run);
+
+struct NRSlice {
+ NRSlice *next;
+ int wind;
+ NRPoint *points;
+ unsigned int current;
+ unsigned int last;
+ NR::Coord x;
+ NR::Coord y;
+ NR::Coord stepx;
+};
+
+static NRSlice *nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y);
+static NRSlice *nr_slice_free_one (NRSlice *s);
+static void nr_slice_free_list (NRSlice *s);
+static NRSlice *nr_slice_insort (NRSlice *start, NRSlice *slice);
+static int nr_slice_compare (NRSlice *l, NRSlice *r);
+
+static void
+nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int iX0, int iY0, int iX1, int iY1,
+ void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data)
+{
+ NR::Coord dX0, dY0, dX1, dY1;
+ NRSlice *slices;
+ unsigned int sidx;
+ int ystart;
+ unsigned char *rowbuffer;
+ int iy0;
+
+ if (!svp || !svp->length) return;
+
+ /* Find starting pixel row */
+ /* g_assert (svl->bbox.y0 == svl->vertex->y); */
+ sidx = 0;
+ while (NR_SVPSEG_IS_FLAT (svp, sidx) && (sidx < svp->length)) sidx += 1;
+ if (sidx >= svp->length) return;
+ ystart = (int) floor (NR_SVPSEG_Y0 (svp, sidx));
+ if (ystart > iY0) {
+ if (ystart >= iY1) return;
+ px += (ystart - iY0) * rs;
+ iY0 = ystart;
+ }
+
+ dX0 = iX0;
+ dY0 = iY0;
+ dX1 = iX1;
+ dY1 = iY1;
+
+ /* Construct initial slice list */
+ slices = NULL;
+ while (sidx < svp->length) {
+ if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
+ NRSVPSegment *seg;
+ /* It is real renderable segment */
+ /* Postpone if starts above initial slice */
+ if (NR_SVPSEG_Y0 (svp, sidx) > dY0) break;
+ seg = svp->segments + sidx;
+ if (seg->wind && (NR_SVPSEG_Y1 (svp, sidx) > dY0)) {
+ /* We are renderable and cross initial slice */
+ NRSlice *newslice;
+ newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, dY0);
+ slices = nr_slice_insort (slices, newslice);
+ }
+ }
+ sidx += 1;
+ }
+ if (!slices && (sidx >= svp->length)) return;
+
+ /* Row buffer */
+ /* fixme: not needed */
+ rowbuffer = px;
+
+ /* Main iteration */
+ for (iy0 = iY0; iy0 < iY1; iy0 += 1) {
+ NR::Coord dy0, dy1;
+ NRSlice *ss, *cs;
+ NRRun *runs;
+ int xstart;
+ float globalval;
+ unsigned char *d;
+ int ix0;
+
+ dy0 = iy0;
+ dy1 = dy0 + 1.0;
+
+ /* Add possible new svls to slice list */
+ while (sidx < svp->length) {
+ if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
+ NRSVPSegment *seg;
+ /* It is real renderable segment */
+ /* Postpone if starts above ending slice */
+ if (NR_SVPSEG_Y0 (svp, sidx) > dy1) break;
+ seg = svp->segments + sidx;
+ if (seg->wind) {
+ NR::Coord y;
+ NRSlice *newslice;
+ /* We are renderable */
+ /* fixme: we should use safely nsvl->vertex->y here */
+ y = MAX (dy0, NR_SVPSEG_Y0 (svp, sidx));
+ newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, y);
+ slices = nr_slice_insort (slices, newslice);
+ }
+ }
+ sidx += 1;
+ }
+ /* Construct runs, stretching slices */
+ /* fixme: This step can be optimized by continuing long runs and adding only new ones (Lauris) */
+ runs = NULL;
+ ss = NULL;
+ cs = slices;
+ while (cs) {
+ /* g_assert (cs->y >= y0); */
+ /* g_assert (cs->y < (y + 1)); */
+ while ((cs->y < dy1) && (cs->current < cs->last)) {
+ NR::Coord rx0, ry0, rx1, ry1;
+ NRRun * newrun;
+ rx0 = cs->x;
+ ry0 = cs->y;
+ if (cs->points[cs->current + 1].y > dy1) {
+ /* The same slice continues */
+ rx1 = rx0 + (dy1 - ry0) * cs->stepx;
+ ry1 = dy1;
+ cs->x = rx1;
+ cs->y = ry1;
+ } else {
+ /* Subpixel height run */
+ cs->current += 1;
+ rx1 = cs->points[cs->current].x;
+ ry1 = cs->points[cs->current].y;
+ cs->x = rx1;
+ cs->y = ry1;
+ if (cs->current < cs->last) {
+ cs->stepx = (cs->points[cs->current + 1].x - rx1) / (cs->points[cs->current + 1].y - ry1);
+ }
+ }
+ newrun = nr_run_new (rx0, ry0, rx1, ry1, cs->wind);
+ /* fixme: we should use walking forward/backward instead */
+ runs = nr_run_insort (runs, newrun);
+ }
+ if (cs->current < cs->last) {
+ ss = cs;
+ cs = cs->next;
+ } else {
+ /* Slice is exhausted */
+ cs = nr_slice_free_one (cs);
+ if (ss) {
+ ss->next = cs;
+ } else {
+ slices = cs;
+ }
+ }
+ }
+ /* Slices are expanded to next scanline */
+ /* Run list is generated */
+ /* Globalval is the sum of all finished runs */
+ globalval = 0.0;
+ if ((runs) && (dX0 < runs->x0)) {
+ /* First run starts right from x0 */
+ xstart = (int) floor (runs->x0);
+ } else {
+ NRRun *sr, *cr;
+ /* First run starts left from x0 */
+ xstart = iX0;
+ sr = NULL;
+ cr = runs;
+ while ((cr) && (cr->x0 < dX0)) {
+ if (cr->x1 <= dX0) {
+ globalval += cr->final;
+ /* Remove exhausted current run */
+ cr = nr_run_free_one (cr);
+ if (sr) {
+ sr->next = cr;
+ } else {
+ runs = cr;
+ }
+ } else {
+ cr->x = dX0;
+ cr->value = (dX0 - cr->x0) * cr->step;
+ sr = cr;
+ cr = cr->next;
+ }
+ }
+ }
+
+ /* Running buffer */
+ d = rowbuffer + bpp * (xstart - iX0);
+
+ for (ix0 = xstart; (runs) && (ix0 < iX1); ix0++) {
+ NR::Coord dx0, dx1;
+ int ix1;
+ NRRun *sr, *cr;
+ float localval;
+ unsigned int fill;
+ float fillstep;
+ int rx1;
+ int c24;
+
+ dx0 = ix0;
+ dx1 = dx0 + 1.0;
+ ix1 = ix0 + 1;
+
+ /* process runs */
+ localval = globalval;
+ sr = NULL;
+ cr = runs;
+ fill = TRUE;
+ fillstep = 0.0;
+ rx1 = iX1;
+ while ((cr) && (cr->x0 < dx1)) {
+ if (cr->x1 <= dx1) {
+ /* Run ends here */
+ /* No fill */
+ fill = FALSE;
+ /* Continue with final value */
+ globalval += cr->final;
+ /* Add initial trapezoid */
+ localval += (float) (0.5 * (cr->x1 - cr->x) * (cr->value + cr->final));
+ /* Add final rectangle */
+ localval += (float) ((dx1 - cr->x1) * cr->final);
+ /* Remove exhausted run */
+ cr = nr_run_free_one (cr);
+ if (sr) {
+ sr->next = cr;
+ } else {
+ runs = cr;
+ }
+ } else {
+ /* Run continues through xnext */
+ if (fill) {
+ if (cr->x0 > ix0) {
+ fill = FALSE;
+ } else {
+ rx1 = MIN (rx1, (int) floor (cr->x1));
+ fillstep += cr->step;
+ }
+ }
+ localval += (float) ((dx1 - cr->x) * (cr->value + (dx1 - cr->x) * cr->step / 2.0));
+ cr->x = dx1;
+ cr->value = (dx1 - cr->x0) * cr->step;
+ sr = cr;
+ cr = cr->next;
+ }
+ }
+ if (fill) {
+ if (cr) rx1 = MIN (rx1, (int) floor (cr->x0));
+ }
+#ifdef NR_VERBOSE
+ if ((localval < -0.01) || (localval > 1.01)) {
+ printf ("Weird localval %g : gv %g\n", localval, globalval); // localizing ok
+ }
+#endif
+ localval = CLAMP (localval, 0.0F, 1.0F);
+ c24 = (int) floor (16777215 * localval + 0.5);
+ if (fill && (rx1 > ix1)) {
+ NRRun *r;
+ int s24;
+ s24 = (int) floor (16777215 * fillstep + 0.5);
+ if ((s24 != 0) || (c24 > 65535)) {
+ run (d, rx1 - ix0, c24, s24, data);
+ }
+ /* We have to rewind run positions as well */
+ for (r = runs; r && (r->x0 < dx1); r = r->next) {
+ r->x = rx1;
+ r->value = (rx1 - r->x0) * r->step;
+ }
+ d += bpp * (rx1 - ix0);
+ ix0 = rx1 - 1;
+ } else {
+ run (d, 1, c24, 0, data);
+ d += bpp;
+ }
+ }
+ nr_run_free_list (runs);
+ rowbuffer += rs;
+ }
+ if (slices) nr_slice_free_list (slices);
+}
+
+/* Slices */
+
+#define NR_SLICE_ALLOC_SIZE 32
+static NRSlice *ffslice = NULL;
+
+static NRSlice *
+nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y)
+{
+ NRSlice *s;
+ NRPoint *p;
+
+ /* g_assert (svl); */
+ /* g_assert (svl->vertex); */
+ /* fixme: not sure, whether correct */
+ /* g_assert (y == NR_COORD_SNAP (y)); */
+ /* Slices startpoints are included, endpoints excluded */
+ /* g_return_val_if_fail (y >= svl->bbox.y0, NULL); */
+ /* g_return_val_if_fail (y < svl->bbox.y1, NULL); */
+
+ s = ffslice;
+
+ if (s == NULL) {
+ int i;
+ s = nr_new (NRSlice, NR_SLICE_ALLOC_SIZE);
+ for (i = 1; i < (NR_SLICE_ALLOC_SIZE - 1); i++) s[i].next = &s[i + 1];
+ s[NR_SLICE_ALLOC_SIZE - 1].next = NULL;
+ ffslice = s + 1;
+ } else {
+ ffslice = s->next;
+ }
+
+ s->next = NULL;
+ s->wind = wind;
+ s->points = points;
+ s->current = 0;
+ s->last = length - 1;
+
+ while ((s->current < s->last) && (s->points[s->current + 1].y <= y)) s->current += 1;
+ p = s->points + s->current;
+
+ if (s->points[s->current].y == y) {
+ s->x = p[0].x;
+ } else {
+ s->x = p[0].x + (p[1].x - p[0].x) * (y - p[0].y) / (p[1].y - p[0].y);
+ }
+ s->y = y;
+ s->stepx = (p[1].x - p[0].x) / (p[1].y - p[0].y);
+
+ return s;
+}
+
+static NRSlice *
+nr_slice_free_one (NRSlice *slice)
+{
+ NRSlice *next;
+ next = slice->next;
+ slice->next = ffslice;
+ ffslice = slice;
+ return next;
+}
+
+static void
+nr_slice_free_list (NRSlice *slice)
+{
+ NRSlice *l;
+
+ if (!slice) return;
+
+ for (l = slice; l->next != NULL; l = l->next);
+
+ l->next = ffslice;
+ ffslice = slice;
+}
+
+static NRSlice *
+nr_slice_insort (NRSlice * start, NRSlice * slice)
+{
+ NRSlice * s, * l;
+
+ if (!start) return slice;
+ if (!slice) return start;
+
+ if (nr_slice_compare (slice, start) <= 0) {
+ slice->next = start;
+ return slice;
+ }
+
+ s = start;
+ for (l = start->next; l != NULL; l = l->next) {
+ if (nr_slice_compare (slice, l) <= 0) {
+ slice->next = l;
+ s->next = slice;
+ return start;
+ }
+ s = l;
+ }
+
+ slice->next = NULL;
+ s->next = slice;
+
+ return start;
+}
+
+static int
+nr_slice_compare (NRSlice *l, NRSlice *r)
+{
+ if (l->y == r->y) {
+ if (l->x < r->x) return -1;
+ if (l->x > r->x) return 1;
+ if (l->stepx < r->stepx) return -1;
+ if (l->stepx > r->stepx) return 1;
+ } else if (l->y > r->y) {
+ unsigned int pidx;
+ NRPoint *p;
+ NR::Coord x, ldx, rdx;
+ /* This is bitch - we have to determine r values at l->y */
+ pidx = 0;
+ while ((pidx < r->last) && (r->points[pidx + 1].y <= l->y)) pidx += 1;
+ /* If v is last vertex, r ends before l starts */
+ if (pidx >= r->last) return 1;
+ p = r->points + pidx;
+ if (p[0].y == l->y) {
+ x = p[0].x;
+ } else {
+ x = p[0].x + (p[1].x - p[0].x) * (l->y - p[0].y) / (p[1].y - p[0].y);
+ }
+ if (l->x < x) return -1;
+ if (l->x > x) return 1;
+ ldx = l->stepx * (p[1].y - p[0].y);
+ rdx = p[1].x - p[0].x;
+ if (ldx < rdx) return -1;
+ if (ldx > rdx) return 1;
+ } else {
+ unsigned int pidx;
+ NRPoint *p;
+ NR::Coord x, ldx, rdx;
+ /* This is bitch - we have to determine l value at r->y */
+ pidx = 0;
+ while ((pidx < l->last) && (l->points[pidx + 1].y <= r->y)) pidx += 1;
+ /* If v is last vertex, l ends before r starts */
+ if (pidx >= l->last) return 1;
+ p = l->points + pidx;
+ if (p[0].y == r->y) {
+ x = p[0].x;
+ } else {
+ x = p[0].x + (p[1].x - p[0].x) * (r->y - p[0].y) / (p[1].y - p[0].y);
+ }
+ if (x < r->x) return -1;
+ if (x > r->x) return 1;
+ ldx = l->stepx * (p[1].y - p[0].y);
+ rdx = p[1].x - p[0].x;
+ if (ldx < rdx) return -1;
+ if (ldx > rdx) return 1;
+ }
+ return 0;
+}
+
+/*
+ * Memory management stuff follows (remember goals?)
+ */
+
+#define NR_RUN_ALLOC_SIZE 32
+static NRRun *ffrun = NULL;
+
+static NRRun *
+nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind)
+{
+ NRRun * r;
+
+ r = ffrun;
+
+ if (r == NULL) {
+ int i;
+ r = nr_new (NRRun, NR_RUN_ALLOC_SIZE);
+ for (i = 1; i < (NR_RUN_ALLOC_SIZE - 1); i++) (r + i)->next = (r + i + 1);
+ (r + NR_RUN_ALLOC_SIZE - 1)->next = NULL;
+ ffrun = r + 1;
+ } else {
+ ffrun = r->next;
+ }
+
+ r->next = NULL;
+
+ if (x0 <= x1) {
+ r->x0 = x0;
+ r->x1 = x1;
+ r->y0 = y0;
+ r->y1 = y1;
+ r->step = (x0 == x1) ? 0.0F : (float) (wind * (y1 - y0) / (x1 - x0));
+ } else {
+ r->x0 = x1;
+ r->x1 = x0;
+ r->y0 = y1;
+ r->y1 = y0;
+ r->step = (float) (wind * (y1 - y0) / (x0 - x1));
+ }
+
+ r->final = (float) (wind * (y1 - y0));
+
+ r->value = 0.0;
+ r->x = r->x0;
+
+ return r;
+}
+
+static NRRun *
+nr_run_free_one (NRRun *run)
+{
+ NRRun *next;
+ next = run->next;
+ run->next = ffrun;
+ ffrun = run;
+ return next;
+}
+
+static void
+nr_run_free_list (NRRun * run)
+{
+ NRRun * l;
+
+ if (!run) return;
+
+ for (l = run; l->next != NULL; l = l->next);
+ l->next = ffrun;
+ ffrun = run;
+}
+
+static NRRun *
+nr_run_insort (NRRun * start, NRRun * run)
+{
+ NRRun * s, * l;
+
+ if (!start) return run;
+ if (!run) return start;
+
+ if (run->x0 < start->x0) {
+ run->next = start;
+ return run;
+ }
+
+ s = start;
+ for (l = start->next; l != NULL; l = l->next) {
+ if (run->x0 < l->x0) {
+ run->next = l;
+ s->next = run;
+ return start;
+ }
+ s = l;
+ }
+
+ s->next = run;
+
+ return start;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :