+/* 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 windstate_t get_horizontal_first_windstate(status_t*status, int x1, int x2)
+{
+ point_t p1 = {x1,status->y};
+ point_t p2 = {x2,status->y};
+ segment_t*left = actlist_find(status->actlist, p1, p2);
+
+ segment_t*a = actlist_right(status->actlist, left);
+ while(a) {
+ if(a->pos.y == status->y) {
+ /* we need to iterate through all segments that received a point in this
+ scanline, as actlist_find above will miss (positively sloped) segments
+ that are to the right of (x1,y) only as long as we don't take the
+ hotpixel re-routing into account
+ TODO: this is inefficient, we should probably be iterating through the
+ hotpixels on this scanline.
+ */
+ if(a->pos.x == x1)
+ left = a;
+ if(a->pos.x > x1)
+ break;
+ }
+ a = a->right;
+ }
+
+ assert(!left || left->fs_out_ok);
+#ifdef DEBUG
+ fprintf(stderr, " fragment %.2f..%.2f\n",
+ x1 * status->gridsize,
+ x2 * 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
+ return left?left->wind:status->windrule->start(status->context);
+}
+
+static windstate_t process_horizontal_fragment(status_t*status, horizontal_t*h, int x1, int x2, windstate_t below)
+{
+ windstate_t above = status->windrule->add(status->context, below, h->fs, h->dir, h->polygon_nr);
+ edgestyle_t*fs = status->windrule->diff(&above, &below);
+
+ segment_dir_t dir = above.is_filled?DIR_DOWN:DIR_UP;
+ point_t p1 = {x1,h->y};
+ point_t p2 = {x2,h->y};
+
+ if(fs) {
+ //append_stroke(status, p1, p2, DIR_INVERT(h->dir), fs);
+ append_stroke(status, p1, p2, dir, fs);
+ }
+#ifdef DEBUG
+ fprintf(stderr, " ...%s (below: (wind_nr=%d, filled=%d), above: (wind_nr=%d, filled=%d) %s %d-%d\n",
+ fs?"storing":"ignoring",
+ below.wind_nr, below.is_filled,
+ above.wind_nr, above.is_filled,
+ dir==DIR_UP?"up":"down", x1, x2);
+#endif
+ 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;
+}
+
+