]> git.imager.perl.org - imager.git/blobdiff - polygon.c
Minor pod changes.
[imager.git] / polygon.c
index 27d2980e71e248d14b6977b3f63908bb7e87e958..271513238f0500da370195caaf4ceecd2d4fae86 100644 (file)
--- a/polygon.c
+++ b/polygon.c
@@ -148,12 +148,40 @@ ss_scanline_init(ss_scanline *ss, int linelen, int linepairs) {
   ss_scanline_reset(ss);
 }
 
+static
+void
+ss_scanline_exorcise(ss_scanline *ss) {
+  myfree(ss->line);
+  myfree(ss->ss_list);
+}
+  
+                    
+
 
 /* returns the number of matches */
 
 static
 int
-lines_in_interval(p_line *lset, int l, p_slice *tllist, pcord cc) {
+lines_in_interval(p_line *lset, int l, p_slice *tllist, pcord minc, pcord maxc) {
+  int k;
+  int count = 0;
+  for(k=0; k<l; k++) {
+    if (lset[k].maxy > minc && lset[k].miny < maxc) {
+      if (lset[k].miny == lset[k].maxy) {
+       POLY_DEB( printf(" HORIZONTAL - skipped\n") );
+      } else {
+       tllist[count].x=p_eval_aty(&lset[k],(minc+maxc)/2.0 );
+       tllist[count].n=k;
+       count++;
+      }
+    }
+  }
+  return count;
+}
+
+static
+int
+lines_in_interval_old(p_line *lset, int l, p_slice *tllist, pcord cc) {
   int k;
   int count = 0;
   for(k=0; k<l; k++) {
@@ -180,18 +208,12 @@ mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
   int k;
   for(k=0; k<count; k+=2) {
     l = lset + tllist[k].n;
-    r = lset + tllist[k+1].n;
 
     if (l->y1 == l->y2) {
       mm_log((1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
       exit(3);
     }
 
-    if (r->y1 == r->y2) {
-      mm_log((1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
-      exit(3);
-    }
-
     l->updown = (l->x1 == l->x2) ?
       0 :
       (l->x1 > l->x2)
@@ -200,6 +222,21 @@ mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
       : 
       (l->y1 > l->y2) ? 1 : -1;
 
+    POLY_DEB( printf("marking left line %d as %s(%d)\n", l->n,
+                    l->updown ?  l->updown == 1 ? "up" : "down" : "vert", l->updown, l->updown)
+             );
+
+    if (k+1 >= count) {
+      mm_log((1, "Invalid polygon spec, odd number of line crossings.\n"));
+      return;
+    }
+
+    r = lset + tllist[k+1].n;
+    if (r->y1 == r->y2) {
+      mm_log((1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
+      exit(3);
+    }
+
     r->updown = (r->x1 == r->x2) ?
       0 :
       (r->x1 > r->x2)
@@ -208,10 +245,8 @@ mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
       : 
       (r->y1 > r->y2) ? 1 : -1;
     
-    POLY_DEB( printf("marking left line %d as %s(%d)\n", l->n,
-                    l->updown ?  l->updown == 1 ? "up" : "down" : "vert", l->updown, l->updown);
-             printf("marking right line %d as %s(%d)\n", r->n,
-                    r->updown ?  r->updown == 1 ? "up" : "down" : "vert", r->updown, r->updown);
+    POLY_DEB( printf("marking right line %d as %s(%d)\n", r->n,
+                    r->updown ?  r->updown == 1 ? "up" : "down" : "vert", r->updown, r->updown)
              );
   }
 }
@@ -226,13 +261,15 @@ saturate(int in) {
   return 0;
 }
 
+typedef void (*scanline_flusher)(i_img *im, ss_scanline *ss, int y, void *ctx);
 
 /* This function must be modified later to do proper blending */
 
 void
-scanline_flush(i_img *im, ss_scanline *ss, int y, i_color *val) {
+scanline_flush(i_img *im, ss_scanline *ss, int y, void *ctx) {
   int x, ch, tv;
   i_color t;
+  i_color *val = (i_color *)ctx;
   for(x=0; x<im->xsize; x++) {
     tv = saturate(ss->line[x]);
     i_gpix(im, x, y, &t);
@@ -497,7 +534,7 @@ render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
 
 
 void
-i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
+i_poly_aa_low(i_img *im, int l, double *x, double *y, void *ctx, scanline_flusher flusher) {
   int i ,k;                    /* Index variables */
   int clc;                     /* Lines inside current interval */
   pcord miny ,maxy;            /* Min and max values of the current slice in the subcord system */
@@ -509,9 +546,17 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
   p_line  *lset;               /* List of lines in polygon */
   p_slice *tllist;             /* List of slices */
 
-  fflush(stdout);
-  setbuf(stdout, NULL);
-   
+  mm_log((1, "i_poly_aa(im %p, l %d, x %p, y %p, ctx %p, flusher %p)\n", im, l, x, y, ctx, flusher));
+
+  for(i=0; i<l; i++) {
+    mm_log((2, "(%.2f, %.2f)\n", x[i], y[i]));
+  }
+
+
+  POLY_DEB(
+          fflush(stdout);
+          setbuf(stdout, NULL);
+          );
 
   tllist   = mymalloc(sizeof(p_slice)*l);
   
@@ -535,20 +580,21 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
   /* loop on intervals */
   for(i=0; i<l-1; i++) {
     int startscan = max( coarse(pset[i].y), 0);
-    int stopscan = min( coarse(pset[i+1].y+15), im->ysize-1);
+    int stopscan = min( coarse(pset[i+1].y+15), im->ysize);
     pcord cc = (pset[i].y + pset[i+1].y)/2;
 
+    if (pset[i].y == pset[i+1].y) {
+      POLY_DEB( printf("current slice thickness = 0 => skipping\n") );
+      continue;
+    }
+
     POLY_DEB(
             printf("current slice is %d: %d to %d ( cpoint %d ) scanlines %d to %d\n", 
                    i, pset[i].y, pset[i+1].y, cc, startscan, stopscan)
             );
     
-    if (pset[i].y == pset[i+1].y) {
-      POLY_DEB( printf("current slice thickness = 0 => skipping\n") );
-      continue;
-    }
     
-    clc = lines_in_interval(lset, l, tllist, cc);
+    clc = lines_in_interval(lset, l, tllist, pset[i].y, pset[i+1].y);
     qsort(tllist, clc, sizeof(p_slice), (int(*)(const void *,const void *))p_compx);
 
     mark_updown_slices(lset, tllist, clc);
@@ -572,11 +618,12 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
       tempy = min(cscl*16+16, pset[i+1].y);
       POLY_DEB( printf("evaluating scan line %d \n", cscl) );
       for(k=0; k<clc-1; k+=2) {
+       POLY_DEB( printf("evaluating slice %d\n", k) );
        render_slice_scanline(&templine, cscl, lset+tllist[k].n, lset+tllist[k+1].n);
       }
       if (16*coarse(tempy) == tempy) {
        POLY_DEB( printf("flushing scan line %d\n", cscl) );
-       scanline_flush(im, &templine, cscl, val);
+       flusher(im, &templine, cscl, ctx);
        ss_scanline_reset(&templine);
       }
       /*
@@ -589,6 +636,165 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
     }
   } /* Intervals */
   if (16*coarse(tempy) != tempy) 
-    scanline_flush(im, &templine, cscl-1, val);
+    flusher(im, &templine, cscl-1, ctx);
+
+  ss_scanline_exorcise(&templine);
+  myfree(pset);
+  myfree(lset);
+  myfree(tllist);
+  
 } /* Function */
 
+void
+i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
+  i_poly_aa_low(im, l, x, y, val, scanline_flush);
+}
+
+struct poly_cfill_state {
+  i_color *fillbuf;
+  i_color *linebuf;
+  int *cover;
+  i_fill_t *fill;
+};
+
+void
+scanline_flush_cfill(i_img *im, ss_scanline *ss, int y, void *ctx) {
+  int x, ch, tv;
+  i_color t;
+  int pos;
+  int left, right;
+  struct poly_cfill_state *state = (struct poly_cfill_state *)ctx;
+  i_color *fillbuf = state->fillbuf;
+  i_color *line = state->linebuf;
+
+  left = 0;
+  while (left < im->xsize && ss->line[left] <= 0)
+    ++left;
+  if (left < im->xsize) {
+    right = im->xsize;
+    /* since going from the left found something, moving from the 
+       right should */
+    while (/* right > left && */ ss->line[right-1] <= 0) 
+      --right;
+    
+    (state->fill->fill_with_color)(state->fill, left, y, right-left, 
+                                   im->channels, fillbuf);
+    i_glin(im, left, right, y, line);
+    pos = 0;
+    if (state->fill->combine) {
+      for (x = left; x < right; ++x) {
+        tv = saturate(ss->line[x]);
+        fillbuf[pos].channel[3] = 
+          fillbuf[pos].channel[3] * tv / 255;
+      }
+      (state->fill->combine)(line, fillbuf, im->channels, right-left);
+      pos++;
+    }
+    else {
+      for (x = left; x < right; ++x) {
+        tv = saturate(ss->line[x]);
+        if (tv) { 
+          if (tv == 255) {
+            line[pos] = fillbuf[pos];
+          }
+          else {
+            i_color *to = line + pos;
+            i_color *from = fillbuf + pos;
+            for (ch = 0; ch < im->channels; ++ch) {
+              to->channel[ch] = (tv * from->channel[ch] + 
+                                 (255 - tv) * to->channel[ch]) / 255;
+            }
+          }
+        }
+        pos++;
+      }
+    }
+    i_plin(im, left, right, y, line);
+  }
+}
+
+struct poly_cfill_state_f {
+  i_fcolor *fillbuf;
+  i_fcolor *linebuf;
+  int *cover;
+  i_fill_t *fill;
+};
+
+void
+scanline_flush_cfill_f(i_img *im, ss_scanline *ss, int y, void *ctx) {
+  int x, ch, tv;
+  int pos;
+  int left, right;
+  struct poly_cfill_state_f *state = (struct poly_cfill_state_f *)ctx;
+  i_fcolor *fillbuf = state->fillbuf;
+  i_fcolor *line = state->linebuf;
+
+  left = 0;
+  while (left < im->xsize && ss->line[left] <= 0)
+    ++left;
+  if (left < im->xsize) {
+    right = im->xsize;
+    /* since going from the left found something, moving from the 
+       right should */
+    while (/* right > left && */ ss->line[right-1] <= 0) 
+      --right;
+    
+    (state->fill->fill_with_fcolor)(state->fill, left, y, right-left, 
+                                    im->channels, fillbuf);
+    i_glinf(im, left, right, y, line);
+    pos = 0;
+    if (state->fill->combinef) {
+      for (x = left; x < right; ++x) {
+        tv = saturate(ss->line[x]);
+        fillbuf[pos].channel[3] = 
+          fillbuf[pos].channel[3] * tv / 255;
+      }
+      (state->fill->combinef)(line, fillbuf, im->channels, right-left);
+      pos++;
+    }
+    else {
+      for (x = left; x < right; ++x) {
+        tv = saturate(ss->line[x]);
+        if (tv) { 
+          if (tv == 255) {
+            line[pos] = fillbuf[pos];
+          }
+          else {
+            i_fcolor *to = line + pos;
+            i_fcolor *from = fillbuf + pos;
+            for (ch = 0; ch < im->channels; ++ch) {
+              to->channel[ch] = (tv * from->channel[ch] + 
+                                 (255 - tv) * to->channel[ch]) / 255;
+            }
+          }
+        }
+        pos++;
+      }
+    }
+    i_plinf(im, left, right, y, line);
+  }
+}
+
+void
+i_poly_aa_cfill(i_img *im, int l, double *x, double *y, i_fill_t *fill) {
+  if (im->bits == i_8_bits && fill->fill_with_color) {
+    struct poly_cfill_state ctx;
+    ctx.fillbuf = mymalloc(sizeof(i_color) * im->xsize * 2);
+    ctx.linebuf = ctx.fillbuf + im->xsize;
+    ctx.cover = mymalloc(sizeof(int) * im->xsize);
+    ctx.fill = fill;
+    i_poly_aa_low(im, l, x, y, &ctx, scanline_flush_cfill);
+    myfree(ctx.fillbuf);
+    myfree(ctx.cover);
+  }
+  else {
+    struct poly_cfill_state_f ctx;
+    ctx.fillbuf = mymalloc(sizeof(i_fcolor) * im->xsize * 2);
+    ctx.linebuf = ctx.fillbuf + im->xsize;
+    ctx.cover = mymalloc(sizeof(int) * im->xsize);
+    ctx.fill = fill;
+    i_poly_aa_low(im, l, x, y, &ctx, scanline_flush_cfill_f);
+    myfree(ctx.fillbuf);
+    myfree(ctx.cover);
+  }
+}