+ return above;
+}
+
+typedef enum {hevent_hotpixel,hevent_end,hevent_start} horizontal_event_type_t;
+typedef struct _hevent {
+ int32_t x;
+ horizontal_t*h;
+ horizontal_event_type_t type;
+} hevent_t;
+
+typedef struct _hevents {
+ hevent_t*events;
+ int num;
+} hevents_t;
+
+static int compare_hevents(const void *_e1, const void *_e2)
+{
+ hevent_t*e1 = (hevent_t*)_e1;
+ hevent_t*e2 = (hevent_t*)_e2;
+ int diff = e1->x - e2->x;
+ if(diff) return diff;
+ return e1->type - e2->type; //schedule hotpixel before hend
+}
+
+static hevents_t hevents_fill(status_t*status)
+{
+ horizdata_t*horiz = &status->horiz;
+ xrow_t*xrow = status->xrow;
+
+ hevents_t e;
+ e.events = malloc(sizeof(hevent_t)*(horiz->num*2 + xrow->num));
+ e.num = 0;
+
+ int t;
+ for(t=0;t<horiz->num;t++) {
+ assert(horiz->data[t].x1 != horiz->data[t].x2);
+ e.events[e.num].x = horiz->data[t].x1;
+ e.events[e.num].h = &horiz->data[t];
+ e.events[e.num].type = hevent_start;
+ e.num++;
+ e.events[e.num].x = horiz->data[t].x2;
+ e.events[e.num].h = &horiz->data[t];
+ e.events[e.num].type = hevent_end;
+ e.num++;
+ }
+ for(t=0;t<xrow->num;t++) {
+ e.events[e.num].x = status->xrow->x[t];
+ e.events[e.num].h = 0;
+ e.events[e.num].type = hevent_hotpixel;
+ e.num++;
+ }
+ qsort(e.events, e.num, sizeof(hevent_t), compare_hevents);
+ return e;
+
+}
+
+static void process_horizontals(status_t*status)
+{
+ horizdata_t*horiz = &status->horiz;
+
+ if(!horiz->num)
+ return;
+
+ hevents_t events = hevents_fill(status);
+ int num_open = 0;
+ horizontal_t**open = malloc(sizeof(horizontal_t*)*horiz->num);
+
+ int s,t;
+ for(t=0;t<events.num;t++) {
+ hevent_t*e = &events.events[t];
+ switch(e->type) {
+ case hevent_start:
+ e->h->pos = num_open;
+ open[num_open++] = e->h;
+#ifdef DEBUG
+ fprintf(stderr, "horizontal (y=%.2f): %.2f -> %.2f dir=%s fs=%p\n",
+ e->h->y * status->gridsize,
+ e->h->x1 * status->gridsize,
+ e->h->x2 * status->gridsize,
+ e->h->dir==DIR_UP?"up":"down", e->h->fs);
+#endif
+ assert(e->h->y == status->y);
+ assert(xrow_contains(status->xrow, e->h->x1));
+ assert(xrow_contains(status->xrow, e->h->x2));
+ break;
+ case hevent_end:
+ num_open--;
+ if(num_open) {
+ open[num_open]->pos = e->h->pos;
+ open[e->h->pos] = open[num_open];
+ }
+ break;
+ case hevent_hotpixel:
+ {
+ windstate_t below;
+ for(s=0;s<num_open;s++) {
+ int x1 = open[s]->xpos;
+ int x2 = e->x;
+ assert(status->y == open[s]->y);
+ if(!s)
+ below = get_horizontal_first_windstate(status, x1, x2);
+ open[s]->xpos = e->x;
+ assert(x1 < x2);
+ below = process_horizontal_fragment(status, open[s], x1, x2, below);
+ }
+ }
+ break;
+ }
+ }
+ free(open);
+ free(events.events);
+}
+
+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;
+ }
+
+ /* TODO: convert this into a linked list */
+ 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->xpos = p1.x;
+ 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
+
+ switch(e->type) {
+ case EVENT_HORIZONTAL: {
+ segment_t*s = e->s1;
+ intersect_with_horizontal(status, s);
+ store_horizontal(status, s->a, s->b, s->fs, s->dir, s->polygon_nr);
+ advance_stroke(&status->queue, 0, s->stroke, s->polygon_nr, s->stroke_pos, status->gridsize);
+ segment_destroy(s);e->s1=0;