1
#define __NR_SVP_RENDER_C__
4
* Pixel buffer rendering library
7
* Lauris Kaplinski <lauris@kaplinski.com>
9
* This code is in public domain
12
#define NR_SVPSEG_Y0(s,i) ((s)->points[(s)->segments[i].start].y)
13
#define NR_SVPSEG_Y1(s,i) ((s)->points[(s)->segments[i].start + (s)->segments[i].length - 1].y)
17
#include "nr-svp-render.h"
19
static void nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int x0, int y0, int x1, int y1,
20
void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data);
22
static void nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data);
24
/* Renders graymask of svl into buffer */
27
nr_pixblock_render_svp_mask_or (NRPixBlock *d, NRSVP *svp)
29
nr_svp_render (svp, NR_PIXBLOCK_PX (d), 1, d->rs,
30
d->area.x0, d->area.y0, d->area.x1, d->area.y1,
31
nr_svp_run_A8_OR, NULL);
35
nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data)
37
if ((c0_24 >= 0xff0000) && (s0_24 == 0x0)) {
49
da = 65025 - (255 - ca) * (255 - d[0]);
50
d[0] = (da + 127) / 255;
53
c0_24 = CLAMP (c0_24, 0, 16777216);
62
NR::Coord x0, y0, x1, y1;
69
static NRRun *nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind);
70
static NRRun *nr_run_free_one (NRRun *run);
71
static void nr_run_free_list (NRRun *run);
72
static NRRun *nr_run_insort (NRRun *start, NRRun *run);
85
static NRSlice *nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y);
86
static NRSlice *nr_slice_free_one (NRSlice *s);
87
static void nr_slice_free_list (NRSlice *s);
88
static NRSlice *nr_slice_insort (NRSlice *start, NRSlice *slice);
89
static int nr_slice_compare (NRSlice *l, NRSlice *r);
92
nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int iX0, int iY0, int iX1, int iY1,
93
void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data)
95
NR::Coord dX0, dY0, dX1, dY1;
99
unsigned char *rowbuffer;
102
if (!svp || !svp->length) return;
104
/* Find starting pixel row */
105
/* g_assert (svl->bbox.y0 == svl->vertex->y); */
107
while (NR_SVPSEG_IS_FLAT (svp, sidx) && (sidx < svp->length)) sidx += 1;
108
if (sidx >= svp->length) return;
109
ystart = (int) floor (NR_SVPSEG_Y0 (svp, sidx));
111
if (ystart >= iY1) return;
112
px += (ystart - iY0) * rs;
121
/* Construct initial slice list */
123
while (sidx < svp->length) {
124
if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
126
/* It is real renderable segment */
127
/* Postpone if starts above initial slice */
128
if (NR_SVPSEG_Y0 (svp, sidx) > dY0) break;
129
seg = svp->segments + sidx;
130
if (seg->wind && (NR_SVPSEG_Y1 (svp, sidx) > dY0)) {
131
/* We are renderable and cross initial slice */
133
newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, dY0);
134
slices = nr_slice_insort (slices, newslice);
139
if (!slices && (sidx >= svp->length)) return;
142
/* fixme: not needed */
146
for (iy0 = iY0; iy0 < iY1; iy0 += 1) {
158
/* Add possible new svls to slice list */
159
while (sidx < svp->length) {
160
if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
162
/* It is real renderable segment */
163
/* Postpone if starts above ending slice */
164
if (NR_SVPSEG_Y0 (svp, sidx) > dy1) break;
165
seg = svp->segments + sidx;
169
/* We are renderable */
170
/* fixme: we should use safely nsvl->vertex->y here */
171
y = MAX (dy0, NR_SVPSEG_Y0 (svp, sidx));
172
newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, y);
173
slices = nr_slice_insort (slices, newslice);
178
/* Construct runs, stretching slices */
179
/* fixme: This step can be optimized by continuing long runs and adding only new ones (Lauris) */
184
/* g_assert (cs->y >= y0); */
185
/* g_assert (cs->y < (y + 1)); */
186
while ((cs->y < dy1) && (cs->current < cs->last)) {
187
NR::Coord rx0, ry0, rx1, ry1;
191
if (cs->points[cs->current + 1].y > dy1) {
192
/* The same slice continues */
193
rx1 = rx0 + (dy1 - ry0) * cs->stepx;
198
/* Subpixel height run */
200
rx1 = cs->points[cs->current].x;
201
ry1 = cs->points[cs->current].y;
204
if (cs->current < cs->last) {
205
cs->stepx = (cs->points[cs->current + 1].x - rx1) / (cs->points[cs->current + 1].y - ry1);
208
newrun = nr_run_new (rx0, ry0, rx1, ry1, cs->wind);
209
/* fixme: we should use walking forward/backward instead */
210
runs = nr_run_insort (runs, newrun);
212
if (cs->current < cs->last) {
216
/* Slice is exhausted */
217
cs = nr_slice_free_one (cs);
225
/* Slices are expanded to next scanline */
226
/* Run list is generated */
227
/* Globalval is the sum of all finished runs */
229
if ((runs) && (dX0 < runs->x0)) {
230
/* First run starts right from x0 */
231
xstart = (int) floor (runs->x0);
234
/* First run starts left from x0 */
238
while ((cr) && (cr->x0 < dX0)) {
240
globalval += cr->final;
241
/* Remove exhausted current run */
242
cr = nr_run_free_one (cr);
250
cr->value = (dX0 - cr->x0) * cr->step;
258
d = rowbuffer + bpp * (xstart - iX0);
260
for (ix0 = xstart; (runs) && (ix0 < iX1); ix0++) {
275
localval = globalval;
281
while ((cr) && (cr->x0 < dx1)) {
286
/* Continue with final value */
287
globalval += cr->final;
288
/* Add initial trapezoid */
289
localval += (float) (0.5 * (cr->x1 - cr->x) * (cr->value + cr->final));
290
/* Add final rectangle */
291
localval += (float) ((dx1 - cr->x1) * cr->final);
292
/* Remove exhausted run */
293
cr = nr_run_free_one (cr);
300
/* Run continues through xnext */
305
rx1 = MIN (rx1, (int) floor (cr->x1));
306
fillstep += cr->step;
309
localval += (float) ((dx1 - cr->x) * (cr->value + (dx1 - cr->x) * cr->step / 2.0));
311
cr->value = (dx1 - cr->x0) * cr->step;
317
if (cr) rx1 = MIN (rx1, (int) floor (cr->x0));
320
if ((localval < -0.01) || (localval > 1.01)) {
321
printf ("Weird localval %g : gv %g\n", localval, globalval); // localizing ok
324
localval = CLAMP (localval, 0.0F, 1.0F);
325
c24 = (int) floor (16777215 * localval + 0.5);
326
if (fill && (rx1 > ix1)) {
329
s24 = (int) floor (16777215 * fillstep + 0.5);
330
if ((s24 != 0) || (c24 > 65535)) {
331
run (d, rx1 - ix0, c24, s24, data);
333
/* We have to rewind run positions as well */
334
for (r = runs; r && (r->x0 < dx1); r = r->next) {
336
r->value = (rx1 - r->x0) * r->step;
338
d += bpp * (rx1 - ix0);
341
run (d, 1, c24, 0, data);
345
nr_run_free_list (runs);
348
if (slices) nr_slice_free_list (slices);
353
#define NR_SLICE_ALLOC_SIZE 32
354
static NRSlice *ffslice = NULL;
357
nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y)
362
/* g_assert (svl); */
363
/* g_assert (svl->vertex); */
364
/* fixme: not sure, whether correct */
365
/* g_assert (y == NR_COORD_SNAP (y)); */
366
/* Slices startpoints are included, endpoints excluded */
367
/* g_return_val_if_fail (y >= svl->bbox.y0, NULL); */
368
/* g_return_val_if_fail (y < svl->bbox.y1, NULL); */
374
s = nr_new (NRSlice, NR_SLICE_ALLOC_SIZE);
375
for (i = 1; i < (NR_SLICE_ALLOC_SIZE - 1); i++) s[i].next = &s[i + 1];
376
s[NR_SLICE_ALLOC_SIZE - 1].next = NULL;
386
s->last = length - 1;
388
while ((s->current < s->last) && (s->points[s->current + 1].y <= y)) s->current += 1;
389
p = s->points + s->current;
391
if (s->points[s->current].y == y) {
394
s->x = p[0].x + (p[1].x - p[0].x) * (y - p[0].y) / (p[1].y - p[0].y);
397
s->stepx = (p[1].x - p[0].x) / (p[1].y - p[0].y);
403
nr_slice_free_one (NRSlice *slice)
407
slice->next = ffslice;
413
nr_slice_free_list (NRSlice *slice)
419
for (l = slice; l->next != NULL; l = l->next);
426
nr_slice_insort (NRSlice * start, NRSlice * slice)
430
if (!start) return slice;
431
if (!slice) return start;
433
if (nr_slice_compare (slice, start) <= 0) {
439
for (l = start->next; l != NULL; l = l->next) {
440
if (nr_slice_compare (slice, l) <= 0) {
455
nr_slice_compare (NRSlice *l, NRSlice *r)
458
if (l->x < r->x) return -1;
459
if (l->x > r->x) return 1;
460
if (l->stepx < r->stepx) return -1;
461
if (l->stepx > r->stepx) return 1;
462
} else if (l->y > r->y) {
465
NR::Coord x, ldx, rdx;
466
/* This is bitch - we have to determine r values at l->y */
468
while ((pidx < r->last) && (r->points[pidx + 1].y <= l->y)) pidx += 1;
469
/* If v is last vertex, r ends before l starts */
470
if (pidx >= r->last) return 1;
471
p = r->points + pidx;
472
if (p[0].y == l->y) {
475
x = p[0].x + (p[1].x - p[0].x) * (l->y - p[0].y) / (p[1].y - p[0].y);
477
if (l->x < x) return -1;
478
if (l->x > x) return 1;
479
ldx = l->stepx * (p[1].y - p[0].y);
480
rdx = p[1].x - p[0].x;
481
if (ldx < rdx) return -1;
482
if (ldx > rdx) return 1;
486
NR::Coord x, ldx, rdx;
487
/* This is bitch - we have to determine l value at r->y */
489
while ((pidx < l->last) && (l->points[pidx + 1].y <= r->y)) pidx += 1;
490
/* If v is last vertex, l ends before r starts */
491
if (pidx >= l->last) return 1;
492
p = l->points + pidx;
493
if (p[0].y == r->y) {
496
x = p[0].x + (p[1].x - p[0].x) * (r->y - p[0].y) / (p[1].y - p[0].y);
498
if (x < r->x) return -1;
499
if (x > r->x) return 1;
500
ldx = l->stepx * (p[1].y - p[0].y);
501
rdx = p[1].x - p[0].x;
502
if (ldx < rdx) return -1;
503
if (ldx > rdx) return 1;
509
* Memory management stuff follows (remember goals?)
512
#define NR_RUN_ALLOC_SIZE 32
513
static NRRun *ffrun = NULL;
516
nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind)
524
r = nr_new (NRRun, NR_RUN_ALLOC_SIZE);
525
for (i = 1; i < (NR_RUN_ALLOC_SIZE - 1); i++) (r + i)->next = (r + i + 1);
526
(r + NR_RUN_ALLOC_SIZE - 1)->next = NULL;
539
r->step = (x0 == x1) ? 0.0F : (float) (wind * (y1 - y0) / (x1 - x0));
545
r->step = (float) (wind * (y1 - y0) / (x0 - x1));
548
r->final = (float) (wind * (y1 - y0));
557
nr_run_free_one (NRRun *run)
567
nr_run_free_list (NRRun * run)
573
for (l = run; l->next != NULL; l = l->next);
579
nr_run_insort (NRRun * start, NRRun * run)
583
if (!start) return run;
584
if (!run) return start;
586
if (run->x0 < start->x0) {
592
for (l = start->next; l != NULL; l = l->next) {
593
if (run->x0 < l->x0) {
609
c-file-style:"stroustrup"
610
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
615
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :