]> git.imager.perl.org - imager.git/blobdiff - polygon.c
avoid an unneeded EXTEND() call when the FT2 has_chars() implementation returns 0.
[imager.git] / polygon.c
index 2a84c8c2a11299d12a6f4af23e1de6d83df9553a..54923b47e96ee8ddd746c1788c0828d56e52f656 100644 (file)
--- a/polygon.c
+++ b/polygon.c
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include "draw.h"
 #include "log.h"
@@ -31,6 +32,7 @@ typedef struct {
   pcord miny,maxy;
   pcord minx,maxx;
   int updown; /* -1 means down, 0 vertical, 1 up */
+  int dir; /* 1 for down, -1 for up */
 } p_line;
 
 typedef struct {
@@ -89,8 +91,6 @@ line_set_new(const i_polygon_t *polys, size_t count, size_t *line_count) {
   for (i = 0; i < count; ++i)
     lines += polys[i].count;
 
-  *line_count = lines;
-
   line = lset = mymalloc(sizeof(p_line) * lines);
 
   n = 0;
@@ -98,18 +98,25 @@ line_set_new(const i_polygon_t *polys, size_t count, size_t *line_count) {
     const i_polygon_t *p = polys + i;
 
     for(j = 0; j < p->count; j++) {
-      line->n = n++;
       line->x1 = IMTRUNC(p->x[j]);
       line->y1 = IMTRUNC(p->y[j]);
       line->x2 = IMTRUNC(p->x[(j + 1) % p->count]);
       line->y2 = IMTRUNC(p->y[(j + 1) % p->count]);
+
+      /* don't include purely horizontal lines, we don't usefully
+        intersect with them. */
+      if (line->y1 == line->y2)
+       continue;
+
       line->miny = i_min(line->y1, line->y2);
       line->maxy = i_max(line->y1, line->y2);
       line->minx = i_min(line->x1, line->x2);
       line->maxx = i_max(line->x1, line->x2);
+      line->n = n++;
       ++line;
     }
   }
+  *line_count = n;
 
   return lset;
 }
@@ -190,14 +197,14 @@ lines_in_interval(p_line *lset, int l, p_slice *tllist, pcord minc, pcord maxc)
 
 static
 void
-mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
+mark_updown_slices(pIMCTX, p_line *lset, p_slice *tllist, int count) {
   p_line *l, *r;
   int k;
   for(k=0; k<count; k+=2) {
     l = lset + tllist[k].n;
 
     if (l->y1 == l->y2) {
-      mm_log((1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
+      im_log((aIMCTX,1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
       exit(3);
     }
 
@@ -208,19 +215,20 @@ mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
       (l->y1 > l->y2) ? -1 : 1
       : 
       (l->y1 > l->y2) ? 1 : -1;
+    l->dir = 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"));
+      im_log((aIMCTX, 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"));
+      im_log((aIMCTX, 1, "mark_updown_slices: horizontal line being marked: internal error!\n"));
       exit(3);
     }
 
@@ -231,6 +239,7 @@ mark_updown_slices(p_line *lset, p_slice *tllist, int count) {
       (r->y1 > r->y2) ? -1 : 1
       : 
       (r->y1 > r->y2) ? 1 : -1;
+    r->dir = r->y1 < r->y2 ? 1 : -1;
     
     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)
@@ -433,7 +442,8 @@ render_slice_scanline(ss_scanline *ss, int y, p_line *l, p_line *r, pcord miny,
 
 static int
 i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
-                  void *ctx, scanline_flusher flusher) {
+                  i_poly_fill_mode_t mode, void *ctx,
+                  scanline_flusher flusher) {
   int i ,k;                    /* Index variables */
   i_img_dim clc;               /* Lines inside current interval */
   /* initialize to avoid compiler warnings */
@@ -445,14 +455,29 @@ i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
   p_line  *lset;               /* List of lines in polygon */
   p_slice *tllist;             /* List of slices */
   size_t pcount, lcount;
+  dIMCTX;
+
+  im_log((aIMCTX, 1, "i_poly_poly_aa_low(im %p, count %d, polys %p, ctx %p, flusher %p)\n", im, count, polys, ctx, flusher));
+
+  i_clear_error();
 
-  mm_log((1, "i_poly_poly_aa_low(im %p, count %d, polys %p, ctx %p, flusher %p)\n", im, count, polys, ctx, flusher));
+  if (count < 1) {
+    i_push_error(0, "no polygons to draw");
+    return 0;
+  }
+
+  for (k = 0; k < count; ++k) {
+    if (polys[k].count < 3) {
+      i_push_error(0, "polygons must have at least 3 points");
+      return 0;
+    }
+  }
 
   for (k = 0; k < count; ++k) {
     const i_polygon_t *p = polys + k;
-    mm_log((2, "poly %d\n", k));
+    im_log((aIMCTX, 2, "poly %d\n", k));
     for(i = 0; i < p->count; i++) {
-      mm_log((2, " (%.2f, %.2f)\n", p->x[i], p->y[i]));
+      im_log((aIMCTX, 2, " (%.2f, %.2f)\n", p->x[i], p->y[i]));
     }
   }
 
@@ -501,7 +526,7 @@ i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
     clc = lines_in_interval(lset, lcount, 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);
+    mark_updown_slices(aIMCTX, lset, tllist, clc);
 
     POLY_DEB
       (
@@ -537,9 +562,28 @@ i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
       
       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) );
-       render_slice_scanline(&templine, cscl, lset+tllist[k].n, lset+tllist[k+1].n, scan_miny, scan_maxy);
+      if (mode == i_pfm_evenodd) {
+       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, scan_miny, scan_maxy);
+       }
+      }
+      else {
+       k = 0;
+       while (k < clc) {
+         p_line *left = lset + tllist[k++].n;
+         p_line *current = NULL;
+         int acc = left->dir;
+
+         while (k < clc && acc) {
+           current = lset + tllist[k++].n;
+           acc += current->dir;
+         }
+         if (acc == 0) {
+           render_slice_scanline(&templine, cscl, left, current,
+                                 scan_miny, scan_maxy);
+         }
+       }
       }
       if (16*coarse(tempy) == tempy) {
        POLY_DEB( printf("flushing scan line %d\n", cscl) );
@@ -566,11 +610,48 @@ i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
   return 1;
 }
 
+/*
+=item i_poly_poly_aa(im, count, polys, mode, color)
+=synopsis i_poly_poly_aa(im, 1, &poly, mode, color);
+=category Drawing
+
+Fill the C<count> polygons defined by C<polys> the color specified by
+C<color>.
+
+At least one polygon must be supplied.
+
+All polygons must have at least 3 points.
+
+=cut
+*/
+
 int
 i_poly_poly_aa(i_img *im, int count, const i_polygon_t *polys,
-              const i_color *val) {
+              i_poly_fill_mode_t mode, const i_color *val) {
   i_color c = *val;
-  return i_poly_poly_aa_low(im, count, polys, &c, scanline_flush);
+  return i_poly_poly_aa_low(im, count, polys, mode, &c, scanline_flush);
+}
+
+/*
+=item i_poly_aa_m(im, count, x, y, mode, color)
+=synopsis i_poly_aa_m(im, count, x, y, mode, color);
+=category Drawing
+
+Fill a polygon defined by the points specified by the x and y arrays with
+the color specified by C<color>.
+
+=cut
+*/
+
+int
+i_poly_aa_m(i_img *im, int l, const double *x, const double *y,
+           i_poly_fill_mode_t mode, const i_color *val) {
+  i_polygon_t poly;
+
+  poly.count = l;
+  poly.x = x;
+  poly.y = y;
+  return i_poly_poly_aa(im, 1, &poly, mode, val);
 }
 
 int
@@ -580,7 +661,7 @@ i_poly_aa(i_img *im, int l, const double *x, const double *y, const i_color *val
   poly.count = l;
   poly.x = x;
   poly.y = y;
-  return i_poly_poly_aa(im, 1, &poly, val);
+  return i_poly_poly_aa(im, 1, &poly, i_pfm_evenodd, val);
 }
 
 struct poly_render_state {
@@ -614,9 +695,24 @@ scanline_flush_render(i_img *im, ss_scanline *ss, int y, void *ctx) {
   }
 }
 
+/*
+=item i_poly_poly_aa_cfill(im, count, polys, mode, fill)
+=synopsis i_poly_poly_aa_cfill(im, 1, &poly, mode, fill);
+=category Drawing
+
+Fill the C<count> polygons defined by C<polys> the fill specified by
+C<fill>.
+
+At least one polygon must be supplied.
+
+All polygons must have at least 3 points.
+
+=cut
+*/
+
 int
 i_poly_poly_aa_cfill(i_img *im, int count, const i_polygon_t *polys,
-                    i_fill_t *fill) {
+                    i_poly_fill_mode_t mode, i_fill_t *fill) {
   struct poly_render_state ctx;
   int result;
 
@@ -624,7 +720,8 @@ i_poly_poly_aa_cfill(i_img *im, int count, const i_polygon_t *polys,
   ctx.fill = fill;
   ctx.cover = mymalloc(im->xsize);
 
-  result = i_poly_poly_aa_low(im, count, polys, &ctx, scanline_flush_render);
+  result = i_poly_poly_aa_low(im, count, polys, mode, &ctx,
+                             scanline_flush_render);
 
   myfree(ctx.cover);
   i_render_done(&ctx.render);
@@ -632,6 +729,29 @@ i_poly_poly_aa_cfill(i_img *im, int count, const i_polygon_t *polys,
   return result;
 }
 
+/*
+=item i_poly_aa_cfill_m(im, count, x, y, mode, fill)
+=synopsis i_poly_aa_cfill(im, count, x, y, mode, fill);
+=category Drawing
+
+Fill a polygon defined by the points specified by the x and y arrays with
+the fill specified by C<fill>.
+
+=cut
+*/
+
+int
+i_poly_aa_cfill_m(i_img *im, int l, const double *x, const double *y, 
+               i_poly_fill_mode_t mode, i_fill_t *fill) {
+  i_polygon_t poly;
+
+  poly.count = l;
+  poly.x = x;
+  poly.y = y;
+
+  return i_poly_poly_aa_cfill(im, 1, &poly, mode, fill);
+}
+
 int
 i_poly_aa_cfill(i_img *im, int l, const double *x, const double *y, 
                i_fill_t *fill) {
@@ -641,5 +761,5 @@ i_poly_aa_cfill(i_img *im, int l, const double *x, const double *y,
   poly.x = x;
   poly.y = y;
 
-  return i_poly_poly_aa_cfill(im, 1, &poly, fill);
+  return i_poly_poly_aa_cfill(im, 1, &poly, i_pfm_evenodd, fill);
 }