+ segment_t*seg = status->ending_segments;
+ while(seg) {
+ segment_t*next = seg->right;seg->right=0;
+
+ assert(seg->b.y == status->y);
+
+ if(status->xrow->num == 1) {
+ // shortcut
+ assert(seg->b.x == status->xrow->x[0]);
+ point_t p = {status->xrow->x[0], y};
+ insert_point_into_segment(status, seg, p);
+ } else {
+ int t;
+ int start=0,end=status->xrow->num,dir=1;
+ if(seg->delta.x < 0) {
+ start = status->xrow->num-1;
+ end = dir = -1;
+ }
+ for(t=start;t!=end;t+=dir) {
+ box_t box = box_new(status->xrow->x[t], y);
+ double d0 = LINE_EQ(box.left1, seg);
+ double d1 = LINE_EQ(box.left2, seg);
+ double d2 = LINE_EQ(box.right1, seg);
+ double d3 = LINE_EQ(box.right2, seg);
+ if(!(d0>=0 && d1>=0 && d2>=0 && d3>0 ||
+ d0<=0 && d1<=0 && d2<=0 && d3<0)) {
+ insert_point_into_segment(status, seg, box.right2);
+ break;
+ }
+ }
+
+#ifdef CHECKS
+ /* we *need* to find a point to insert. the segment's own end point
+ is in that list, for Pete's sake. */
+ assert(t!=end);
+#endif
+ }
+ // now that this is done, too, we can also finally free this segment
+ segment_destroy(seg);
+ seg = next;
+ }
+ status->ending_segments = 0;
+}
+
+static void recalculate_windings(status_t*status, segrange_t*range)
+{
+#ifdef DEBUG
+ fprintf(stderr, "range: [%d]..[%d]\n", SEGNR(range->segmin), SEGNR(range->segmax));
+#endif
+ segrange_adjust_endpoints(range, status->y);
+
+ segment_t*s = range->segmin;
+ segment_t*end = range->segmax;
+ segment_t*last = 0;
+
+#ifdef DEBUG
+ s = actlist_leftmost(status->actlist);
+ while(s) {
+ fprintf(stderr, "[%d]%d%s ", s->nr, s->changed,
+ s == range->segmin?"S":(
+ s == range->segmax?"E":""));
+ s = s->right;
+ }
+ fprintf(stderr, "\n");
+ s = range->segmin;
+#endif
+#ifdef CHECKS
+ /* test sanity: verify that we don't have changed segments
+ outside of the given range */
+ s = actlist_leftmost(status->actlist);
+ while(s && s!=range->segmin) {
+ assert(!s->changed);
+ s = s->right;
+ }
+ s = actlist_rightmost(status->actlist);
+ while(s && s!=range->segmax) {
+ assert(!s->changed);
+ s = s->left;
+ }
+ /* in check mode, go through the whole interval so we can test
+ that all polygons where the edgestyle changed also have seg->changed=1 */
+ s = actlist_leftmost(status->actlist);
+ end = 0;
+#endif
+
+ if(end)
+ end = end->right;
+ while(s!=end) {
+#ifndef CHECKS
+ if(s->changed)
+#endif
+ {
+ segment_t* left = actlist_left(status->actlist, s);
+ windstate_t wind = left?left->wind:status->windrule->start(status->context);
+ s->wind = status->windrule->add(status->context, wind, s->fs, s->dir, s->polygon_nr);
+ edgestyle_t*fs_old = s->fs_out;
+ s->fs_out = status->windrule->diff(&wind, &s->wind);
+
+#ifdef DEBUG
+ fprintf(stderr, "[%d] dir=%s wind=%d wind.filled=%s fs_old/new=%s/%s %s\n", s->nr, s->dir==DIR_UP?"up":"down", s->wind.wind_nr, s->wind.is_filled?"fill":"nofill",
+ fs_old?"draw":"omit", s->fs_out?"draw":"omit",
+ fs_old!=s->fs_out?"CHANGED":"");
+#endif
+ assert(!(!s->changed && fs_old!=s->fs_out));
+ s->changed = 0;
+
+#ifdef CHECKS
+ s->fs_out_ok = 1;
+#endif
+ }
+ s = s->right;
+ }
+}
+
+/* we need to handle horizontal lines in order to add points to segments
+ we otherwise would miss during the windrule re-evaluation */
+static void intersect_with_horizontal(status_t*status, segment_t*h)
+{
+ segment_t* left = actlist_find(status->actlist, h->a, h->a);
+ segment_t* right = actlist_find(status->actlist, h->b, h->b);
+
+ /* not strictly necessary, also done by the event */
+ xrow_add(status->xrow, h->a.x);
+
+ if(!right) {
+ assert(!left);
+ return;
+ }
+
+ left = actlist_right(status->actlist, left); //first seg to the right of h->a
+ right = right->right; //first seg to the right of h->b
+ segment_t* s = left;
+
+ point_t o = h->a;
+ while(s!=right) {
+ assert(s);
+ int32_t x = XPOS_INT(s, status->y);
+ point_t p = {x, status->y};
+#ifdef DEBUG
+ fprintf(stderr, "...intersecting with [%d] (%.2f,%.2f) -> (%.2f,%.2f) at (%.2f,%.2f)\n",
+ s->nr,
+ s->a.x * status->gridsize, s->a.y * status->gridsize,
+ s->b.x * status->gridsize, s->b.y * status->gridsize,
+ x * status->gridsize, status->y * status->gridsize
+ );
+#endif
+ assert(x >= h->a.x);
+ assert(x <= h->b.x);
+ assert(s->delta.x > 0 && x >= s->a.x || s->delta.x <= 0 && x <= s->a.x);
+ assert(s->delta.x > 0 && x <= s->b.x || s->delta.x <= 0 && x >= s->b.x);
+ xrow_add(status->xrow, x);
+
+ o = p;
+ s = s->right;
+ }
+}
+
+/* while, for a scanline, we need both starting as well as ending segments in order
+ to *reconstruct* horizontal lines, we only need one or the other to *process*
+ horizontal lines from the input data.
+
+ So horizontal lines are processed twice: first they create hotpixels by intersecting
+ all segments on the scanline (EVENT_HORIZTONAL). Secondly, they are processed for
+ their actual content. The second also happens for all segments that received more than
+ one point in this scanline.
+*/
+void horiz_reset(horizdata_t*horiz)
+{
+ horiz->num = 0;
+}
+
+void horiz_destroy(horizdata_t*horiz)
+{
+ if(horiz->data) rfx_free(horiz->data);
+ horiz->data = 0;
+}
+
+static int compare_horizontals(const void *_h1, const void *_h2)
+{
+ horizontal_t*h1 = (horizontal_t*)_h1;
+ horizontal_t*h2 = (horizontal_t*)_h2;
+ return h1->x1 - h2->x1;
+}
+
+static void process_horizontals(status_t*status)
+{
+ horizdata_t*horiz = &status->horiz;
+ qsort(horiz->data, horiz->num, sizeof(horizontal_t), compare_horizontals);
+ int t;
+ int xstart = 0;
+ for(t=0;t<horiz->num;t++) {
+ horizontal_t*h = &horiz->data[t];
+#ifdef DEBUG
+ fprintf(stderr, "horizontal (y=%.2f): %.2f -> %.2f dir=%s fs=%p\n",
+ h->y * status->gridsize,
+ h->x1 * status->gridsize,
+ h->x2 * status->gridsize,
+ h->dir==DIR_UP?"up":"down", h->fs);
+#endif
+ assert(h->y == status->y);
+ assert(xrow_contains(status->xrow, h->x1));
+ assert(xrow_contains(status->xrow, h->x2));
+
+ int pos = xrow_find(status->xrow, h->x1);
+ int x = h->x1;
+ assert(pos <= status->xrow->num);
+ assert(pos == status->xrow->num || status->xrow->x[pos] > x);
+
+ while(x < h->x2) {
+ int next_x = pos < status->xrow->num ? status->xrow->x[pos] : h->x2;
+ pos++;
+
+ assert(next_x > x);
+
+ point_t p1 = {x,h->y};
+ point_t p2 = {next_x,h->y};
+ segment_t* left = actlist_find(status->actlist, p1, p2);
+ assert(!left || left->fs_out_ok);
+#ifdef DEBUG
+ fprintf(stderr, " fragment %.2f..%.2f\n",
+ x * status->gridsize,
+ next_x * status->gridsize);
+ if(left) {
+ fprintf(stderr, " segment [%d] (%.2f,%.2f -> %.2f,%2f, at %.2f,%.2f) is to the left\n",
+ SEGNR(left),
+ left->a.x * status->gridsize,
+ left->a.y * status->gridsize,
+ left->b.x * status->gridsize,
+ left->b.y * status->gridsize,
+ left->pos.x * status->gridsize,
+ left->pos.y * status->gridsize
+ );
+ /* this segment might be a distance away from the left point
+ of the horizontal line if the horizontal line belongs to a stroke
+ with segments that just ended (so this horizontal line appears to
+ be "floating in space" from our current point of view)
+ assert(left->pos.y == h->y && left->pos.x == h->x1);
+ */
+ }
+#endif
+ windstate_t below = left?left->wind:status->windrule->start(status->context);
+ windstate_t above = status->windrule->add(status->context, below, h->fs, h->dir, h->polygon_nr);
+ edgestyle_t*fs = status->windrule->diff(&above, &below);
+ if(fs) {
+#ifdef DEBUG
+ fprintf(stderr, " ...storing\n");
+#endif
+ append_stroke(status, p1, p2, h->dir, fs);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, " ...ignoring\n");
+#endif
+ }
+ x = next_x;
+ }
+ }
+}
+
+static void store_horizontal(status_t*status, point_t p1, point_t p2, edgestyle_t*fs, segment_dir_t dir, int polygon_nr)
+{
+ assert(p1.y == p2.y);
+ assert(p1.x != p2.x); // TODO: can this happen?
+
+ if(p1.x > p2.x) {
+ dir = DIR_INVERT(dir);
+ point_t p_1 = p1;
+ point_t p_2 = p2;
+ p1 = p_2;
+ p2 = p_1;
+ }
+
+ if(status->horiz.size == status->horiz.num) {
+ if(!status->horiz.size)
+ status->horiz.size = 16;
+ status->horiz.size *= 2;
+ status->horiz.data = rfx_realloc(status->horiz.data, sizeof(status->horiz.data[0])*status->horiz.size);
+ }
+ horizontal_t*h = &status->horiz.data[status->horiz.num++];
+ h->y = p1.y;
+ h->x1 = p1.x;
+ h->x2 = p2.x;
+ h->fs = fs;
+ h->dir = dir;
+ h->polygon_nr = polygon_nr;
+}
+
+
+static void event_apply(status_t*status, event_t*e)
+{
+#ifdef DEBUG
+ event_dump(status, e);
+#endif
+