]> git.imager.perl.org - imager.git/blobdiff - polygon.c
add to_rgb16 method
[imager.git] / polygon.c
index b6338902947858722d061d7c43976b294e96c4eb..9a8b8b2a84255b7ea3b42b0200258a897914c962 100644 (file)
--- a/polygon.c
+++ b/polygon.c
@@ -1,4 +1,4 @@
-#include "image.h"
+#include "imager.h"
 #include "draw.h"
 #include "log.h"
 
@@ -91,7 +91,7 @@ p_eval_atx(p_line *l, pcord x) {
 
 static
 p_line *
-line_set_new(double *x, double *y, int l) {
+line_set_new(const double *x, const double *y, int l) {
   int i;
   p_line *lset = mymalloc(sizeof(p_line) * l);
 
@@ -101,17 +101,17 @@ line_set_new(double *x, double *y, int l) {
     lset[i].y1 = IMTRUNC(y[i]);
     lset[i].x2 = IMTRUNC(x[(i+1)%l]);
     lset[i].y2 = IMTRUNC(y[(i+1)%l]);
-    lset[i].miny=min(lset[i].y1,lset[i].y2);
-    lset[i].maxy=max(lset[i].y1,lset[i].y2);
-    lset[i].minx=min(lset[i].x1,lset[i].x2);
-    lset[i].maxx=max(lset[i].x1,lset[i].x2);
+    lset[i].miny=i_min(lset[i].y1,lset[i].y2);
+    lset[i].maxy=i_max(lset[i].y1,lset[i].y2);
+    lset[i].minx=i_min(lset[i].x1,lset[i].x2);
+    lset[i].maxx=i_max(lset[i].x1,lset[i].x2);
   }
   return lset;
 }
 
 static
 p_point *
-point_set_new(double *x, double *y, int l) {
+point_set_new(const double *x, const double *y, int l) {
   int i;
   p_point *pset = mymalloc(sizeof(p_point) * l);
   
@@ -123,13 +123,14 @@ point_set_new(double *x, double *y, int l) {
   return pset;
 }
 
+#if 0
 static
 void
 p_line_dump(p_line *l) {
   printf("%d (%d,%d)->(%d,%d) [%d-%d,%d-%d]\n", l->n, l->x1, l->y1, l->x2, l->y2, 
         l->minx, l->maxx, l->miny, l->maxy);
 }
-
+#endif
 
 static
 void
@@ -179,6 +180,7 @@ lines_in_interval(p_line *lset, int l, p_slice *tllist, pcord minc, pcord maxc)
   return count;
 }
 
+#if 0
 static
 int
 lines_in_interval_old(p_line *lset, int l, p_slice *tllist, pcord cc) {
@@ -198,6 +200,7 @@ lines_in_interval_old(p_line *lset, int l, p_slice *tllist, pcord cc) {
   }
   return count;
 }
+#endif
 
 /* marks the up variable for all lines in a slice */
 
@@ -261,13 +264,15 @@ saturate(int in) {
   return 0;
 }
 
+typedef void (*scanline_flusher)(i_img *im, ss_scanline *ss, int y, const 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) {
+static void
+scanline_flush(i_img *im, ss_scanline *ss, int y, const 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);
@@ -300,8 +305,6 @@ pixel_coverage(p_line *line, pcord minx, pcord maxx, pcord  miny, pcord maxy) {
   double lycross, rycross;
   int l, r;
 
-  double xs, ys;
-  
   if (!line->updown) {
     l = r = 0;
   } else {
@@ -336,6 +339,8 @@ pixel_coverage(p_line *line, pcord minx, pcord maxx, pcord  miny, pcord maxy) {
       (maxx-p_eval_aty(line, miny))*(p_eval_atx(line, maxx)-miny)/2.0;
     return r;
   }
+
+  return 0; /* silence compiler warning */
 }
 
 
@@ -362,26 +367,25 @@ render_slice_scanline(ss_scanline *ss, int y, p_line *l, p_line *r) {
   int thin;            /* boolean for thin/thick segment */
   int startpix;                /* temporary variable for "start of this interval" */
   int stoppix;         /* temporary variable for "end of this interval" */
-  int step2end;                /* temporary variable to mark where step2 ends */
 
   /* Find the y bounds of scanline_slice */
 
-  maxy = min( l->maxy, r->maxy );
-  miny = max( l->miny, r->miny );
+  maxy = i_min( l->maxy, r->maxy );
+  miny = i_max( l->miny, r->miny );
 
-  maxy = min( maxy, (y+1)*16 );
-  miny = max( miny,  y*16 );
+  maxy = i_min( maxy, (y+1)*16 );
+  miny = i_max( miny,  y*16 );
 
-  lminx = min( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
-  lmaxx = max( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
+  lminx = i_min( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
+  lmaxx = i_max( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
 
-  rminx = min( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
-  rmaxx = max( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
+  rminx = i_min( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
+  rmaxx = i_max( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
 
   thin = coarse(lmaxx) >= coarse(rminx);
 
-  startpix = max( coarse(lminx), 0 );
-  stoppix  = min( coarse(rmaxx-1), ss->linelen-1 );
+  startpix = i_max( coarse(lminx), 0 );
+  stoppix  = i_min( coarse(rmaxx-1), ss->linelen-1 );
   
   for(cpix=startpix; cpix<=stoppix; cpix++) {
     int lt = coarse(lmaxx-1) >= cpix;
@@ -404,7 +408,7 @@ render_slice_scanline(ss_scanline *ss, int y, p_line *l, p_line *r) {
 }
 
 
-
+#if 0
 static
 void
 render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
@@ -416,28 +420,27 @@ render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
   int thin;            /* boolean for thin/thick segment */
   int startpix;                /* temporary variable for "start of this interval" */
   int stoppix;         /* temporary variable for "end of this interval" */
-  int step2end;                /* temporary variable to mark where step2 ends */
 
   /* Find the y bounds of scanline_slice */
 
-  maxy = min( l->maxy, r->maxy );
-  miny = max( l->miny, r->miny );
+  maxy = i_min( l->maxy, r->maxy );
+  miny = i_max( l->miny, r->miny );
 
-  maxy = min( maxy, (y+1)*16 );
-  miny = max( miny,  y*16 );
+  maxy = i_min( maxy, (y+1)*16 );
+  miny = i_max( miny,  y*16 );
 
-  lminx = min( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
-  lmaxx = max( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
+  lminx = i_min( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
+  lmaxx = i_max( p_eval_aty(l, maxy), p_eval_aty(l, miny) );
 
-  rminx = min( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
-  rmaxx = max( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
+  rminx = i_min( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
+  rmaxx = i_max( p_eval_aty(r, maxy), p_eval_aty(r, miny) );
 
   thin = coarse(lmaxx) >= coarse(rminx);
 
 
   /* First step */
   startpix = coarse(lminx);                            /* includes tricky starting pixel */
-  stoppix  = min(coarse(lmaxx), coarse(rminx) );       /* last pixel is tricky */
+  stoppix  = i_min(coarse(lmaxx), coarse(rminx) );     /* last pixel is tricky */
   
   /* handle start pixel */
 
@@ -466,7 +469,7 @@ render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
       printf("%2d: step2a pixel\n", cpix);
       ss->line[cpix] += 
        pixel_coverage(l, cpix*16, cpix*16+16, miny, maxy)
-       +(cpix*16+16-min(cpix*16+16, l->maxx))*(maxy-miny)
+       +(cpix*16+16-i_min(cpix*16+16, l->maxx))*(maxy-miny)
        -pixel_coverage(r, cpix*16, cpix*16+16, miny, maxy);
     }
   } else { /* step 2b */
@@ -479,7 +482,7 @@ render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
   
   /* step 3 */
 
-  cpix = max(coarse(rminx), coarse(lmaxx+15));
+  cpix = i_max(coarse(rminx), coarse(lmaxx+15));
   stoppix = coarse(rmaxx-15);
   
   printf("step3 from %d to %d\n", cpix, stoppix);
@@ -495,11 +498,7 @@ render_slice_scanline_old(ss_scanline *ss, int y, p_line *l, p_line *r) {
   
   ss->line[cpix] += (16.0)*(maxy-miny) - pixel_coverage(r, cpix*16, cpix*16+16, miny, maxy);
 }
-
-
-
-
-
+#endif
 
 /* Antialiasing polygon algorithm 
    specs:
@@ -531,20 +530,20 @@ 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) {
+static void
+i_poly_aa_low(i_img *im, int l, const double *x, const double *y, void const *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 */
-  pcord tempy;
-  int cscl;                    /* Current scanline */
+  /* initialize to avoid compiler warnings */
+  pcord tempy = 0;
+  int cscl = 0;                        /* Current scanline */
 
   ss_scanline templine;                /* scanline accumulator */
   p_point *pset;               /* List of points in polygon */
   p_line  *lset;               /* List of lines in polygon */
   p_slice *tllist;             /* List of slices */
 
-  mm_log((1, "i_poly_aa(im %p, l %d, x %p, y %p, val %p)\n", im, l, x, y, val));
+  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]));
@@ -577,9 +576,8 @@ 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);
-    pcord cc = (pset[i].y + pset[i+1].y)/2;
+    int startscan = i_max( coarse(pset[i].y), 0);
+    int stopscan = i_min( coarse(pset[i+1].y+15), im->ysize);
 
     if (pset[i].y == pset[i+1].y) {
       POLY_DEB( printf("current slice thickness = 0 => skipping\n") );
@@ -600,8 +598,6 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
     POLY_DEB( printf("Interval contains %d lines\n", clc) );
 
     for(k=0; k<clc; k++) {
-      int lno = tllist[k].n;
-      p_line *ln = lset+lno;
       POLY_DEB(
               printf("%d:  line #%2d: (%2d, %2d)->(%2d, %2d) (%2d/%2d, %2d/%2d) -> (%2d/%2d, %2d/%2d) alignment=%s\n",
                      k, lno, ln->x1, ln->y1, ln->x2, ln->y2, 
@@ -613,7 +609,7 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
               );
     }
     for(cscl=startscan; cscl<stopscan; cscl++) {
-      tempy = min(cscl*16+16, pset[i+1].y);
+      tempy = i_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) );
@@ -621,7 +617,7 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
       }
       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);
       }
       /*
@@ -634,7 +630,7 @@ 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);
@@ -642,3 +638,156 @@ i_poly_aa(i_img *im, int l, double *x, double *y, i_color *val) {
   myfree(tllist);
   
 } /* Function */
+
+void
+i_poly_aa(i_img *im, int l, const double *x, const double *y, const 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;
+};
+
+static void
+scanline_flush_cfill(i_img *im, ss_scanline *ss, int y, const void *ctx) {
+  int x, ch, tv;
+  int pos;
+  int left, right;
+  struct poly_cfill_state const *state = (struct poly_cfill_state const *)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;
+       pos++;
+      }
+      (state->fill->combine)(line, fillbuf, im->channels, right-left);
+    }
+    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;
+};
+
+static void
+scanline_flush_cfill_f(i_img *im, ss_scanline *ss, int y, const void *ctx) {
+  int x, ch, tv;
+  int pos;
+  int left, right;
+  struct poly_cfill_state_f const *state = (struct poly_cfill_state_f const *)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;
+       pos++;
+      }
+      (state->fill->combinef)(line, fillbuf, im->channels, right-left);
+    }
+    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, const double *x, const 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);
+  }
+}