access to poly_poly from perl as polypolygon()
authorTony Cook <tony@develop-help.com>
Wed, 23 Apr 2014 12:34:44 +0000 (22:34 +1000)
committerTony Cook <tony@develop-help.com>
Sun, 25 Jan 2015 03:28:54 +0000 (14:28 +1100)
15 files changed:
Imager.pm
Imager.xs
MANIFEST
imager.h
imdatatypes.h
imext.c
imext.h
imexttypes.h
lib/Imager/APIRef.pod
lib/Imager/Draw.pod
polygon.c
t/250-draw/050-polyaa.t
t/250-draw/060-polypoly.t [new file with mode: 0644]
t/450-api/100-inline.t
typemap.local

index 4ff2d74..4db2139 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -3064,6 +3064,8 @@ sub polygon {
     $self->{ERRSTR} = 'no points array, or x and y arrays.'; return undef;
   }
 
+  my $mode = _first($opts{mode}, 0);
+
   if ($opts{'fill'}) {
     unless (UNIVERSAL::isa($opts{'fill'}, 'Imager::Fill')) {
       # assume it's a hash ref
@@ -3073,8 +3075,8 @@ sub polygon {
         return undef;
       }
     }
-    i_poly_aa_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, 
-                    $opts{'fill'}{'fill'});
+    i_poly_aa_cfill_m($self->{IMG}, $opts{'x'}, $opts{'y'}, 
+                    $mode, $opts{'fill'}{'fill'});
   }
   else {
     my $color = _color($opts{'color'});
@@ -3082,12 +3084,73 @@ sub polygon {
       $self->{ERRSTR} = $Imager::ERRSTR; 
       return; 
     }
-    i_poly_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
+    i_poly_aa_m($self->{IMG}, $opts{'x'}, $opts{'y'}, $mode, $color);
   }
 
   return $self;
 }
 
+sub polypolygon {
+  my ($self, %opts) = @_;
+
+  $self->_valid_image("polypolygon")
+    or return;
+
+  my $points = $opts{points};
+  $points
+    or return $self->_set_error("polypolygon: missing required points");
+
+  my $mode = _first($opts{mode}, "evenodd");
+
+  if ($opts{filled}) {
+    my $color = _color(_first($opts{color}, [ 0, 0, 0, 0 ]))
+      or return $self->_set_error($Imager::ERRSTR);
+
+    i_poly_poly_aa($self->{IMG}, $points, $mode, $color)
+      or return $self->_set_error($self->_error_as_msg);
+  }
+  elsif ($opts{fill}) {
+    my $fill = $opts{fill};
+    $self->_valid_fill($fill, "polypolygon")
+      or return;
+
+    i_poly_poly_aa_cfill($self->{IMG}, $points, $mode, $fill->{fill})
+      or return $self->_set_error($self->_error_as_msg);
+  }
+  else {
+    my $color = _color(_first($opts{color}, [ 0, 0, 0, 255 ]))
+      or return $self->_set_error($Imager::ERRSTR);
+
+    my $rimg = $self->{IMG};
+
+    if (_first($opts{aa}, 1)) {
+      for my $poly (@$points) {
+       my $xp = $poly->[0];
+       my $yp = $poly->[1];
+       for my $i (0 .. $#$xp - 1) {
+         i_line_aa($rimg, $xp->[$i], $yp->[$i], $xp->[$i+1], $yp->[$i+1],
+                   $color, 0);
+       }
+       i_line_aa($rimg, $xp->[$#$xp], $yp->[$#$yp], $xp->[0], $yp->[0],
+                 $color, 0);
+      }
+    }
+    else {
+      for my $poly (@$points) {
+       my $xp = $poly->[0];
+       my $yp = $poly->[1];
+       for my $i (0 .. $#$xp - 1) {
+         i_line($rimg, $xp->[$i], $yp->[$i], $xp->[$i+1], $yp->[$i+1],
+                $color, 0);
+       }
+       i_line($rimg, $xp->[$#$xp], $yp->[$#$yp], $xp->[0], $yp->[0],
+              $color, 0);
+      }
+    }
+  }
+
+  return $self;
+}
 
 # this the multipoint bezier curve
 # this is here more for testing that actual usage since
@@ -4753,6 +4816,8 @@ polygon() - L<Imager::Draw/polygon()>
 
 polyline() - L<Imager::Draw/polyline()>
 
+polypolygon() - L<Imager::Draw/polypolygon()>
+
 preload() - L<Imager::Files/preload()>
 
 read() - L<Imager::Files/read()> - read a single image from an image file
index 6847b02..5fe6b4d 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -30,6 +30,8 @@ extern "C" {
 
 #include "imperl.h"
 
+#define ARRAY_COUNT(array) (sizeof(array)/sizeof(*array))
+
 /*
 
 Context object management
@@ -109,6 +111,11 @@ typedef struct {
   const i_fsample_t *samples;
 } i_fsample_list;
 
+typedef struct {
+  size_t count;
+  const i_polygon_t *polygons;
+} i_polygon_list;
+
 /*
 
 Allocate memory that will be discarded when mortals are discarded.
@@ -539,7 +546,7 @@ struct value_name {
   char *name;
   int value;
 };
-static int lookup_name(struct value_name *names, int count, char *name, int def_value)
+static int lookup_name(const struct value_name *names, int count, char *name, int def_value)
 {
   int i;
   for (i = 0; i < count; ++i)
@@ -767,6 +774,95 @@ ip_copy_colors_back(pTHX_ HV *hv, i_quantize *quant) {
   }
 }
 
+static struct value_name
+poly_fill_mode_names[] =
+{
+  { "evenodd", i_pfm_evenodd },
+  { "nonzero", i_pfm_nonzero }
+};
+
+static i_poly_fill_mode_t
+S_get_poly_fill_mode(pTHX_ SV *sv) {   
+  if (looks_like_number(sv)) {
+    IV work = SvIV(sv);
+    if (work < (IV)i_pfm_evenodd || work > (IV)i_pfm_nonzero)
+      work = (IV)i_pfm_evenodd;
+    return (i_poly_fill_mode_t)work;
+  }
+  else {
+    return (i_poly_fill_mode_t)lookup_name
+    (poly_fill_mode_names, ARRAY_COUNT(poly_fill_mode_names),
+     SvPV_nolen(sv), i_pfm_evenodd);
+  }
+}
+
+static void
+S_get_polygon_list(pTHX_ i_polygon_list *polys, SV *sv) {
+  AV *av;
+  int i;
+  i_polygon_t *s;
+
+  SvGETMAGIC(sv);
+  if (!SvOK(sv) || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV) 
+    croak("polys must be an arrayref");
+
+  av = (AV*)SvRV(sv);
+  polys->count = av_len(av) + 1;
+  if (polys->count < 1)
+    croak("polypolygon: no polygons provided");
+  s = malloc_temp(aTHX_ sizeof(i_polygon_t) * polys->count);
+  for (i = 0; i < polys->count; ++i) {
+    SV **poly_sv = av_fetch(av, i, 0);
+    AV *poly_av;
+    SV **x_sv, **y_sv;
+    AV *x_av, *y_av;
+    double *x_data, *y_data;
+    ssize_t j;
+    ssize_t point_count;
+
+    if (!poly_sv)
+      croak("poly_polygon: nothing found for polygon %d", i);
+    /* needs to be another av */
+    SvGETMAGIC(*poly_sv);
+    if (!SvOK(*poly_sv) || !SvROK(*poly_sv) || SvTYPE(SvRV(*poly_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d isn't an arrayref", i);
+    poly_av = (AV*)SvRV(*poly_sv);
+    /* with two elements */
+    if (av_len(poly_av) != 1)
+      croak("poly_polygon: polygon %d should contain two arrays", i);
+    x_sv = av_fetch(poly_av, 0, 0);
+    y_sv = av_fetch(poly_av, 1, 0);
+    if (!x_sv)
+      croak("poly_polygon: polygon %d has no x elements", i);
+    if (!y_sv)
+      croak("poly_polygon: polygon %d has no y elements", i);
+    SvGETMAGIC(*x_sv);
+    SvGETMAGIC(*y_sv);
+    if (!SvOK(*x_sv) || !SvROK(*x_sv) || SvTYPE(SvRV(*x_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d x elements isn't an array", i);
+    if (!SvOK(*y_sv) || !SvROK(*y_sv) || SvTYPE(SvRV(*y_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d y elements isn't an array", i);
+    x_av = (AV*)SvRV(*x_sv);
+    y_av = (AV*)SvRV(*y_sv);
+    if (av_len(x_av) != av_len(y_av))
+      croak("poly_polygon: polygon %d x and y arrays different lengths", i+1);
+    point_count = av_len(x_av)+1;
+    x_data = malloc_temp(aTHX_ sizeof(double) * point_count * 2);
+    y_data = x_data + point_count;
+
+    for (j = 0; j < point_count; ++j) {
+      SV **x_item_sv = av_fetch(x_av, j, 0);
+      SV **y_item_sv = av_fetch(y_av, j, 0);
+      x_data[j] = x_item_sv ? SvNV(*x_item_sv) : 0;
+      y_data[j] = y_item_sv ? SvNV(*y_item_sv) : 0;
+    }
+    s[i].x = x_data;
+    s[i].y = y_data;
+    s[i].count = point_count;
+  }
+  polys->polygons = s;
+}
+
 /* loads the segments of a fountain fill into an array */
 static i_fountain_seg *
 load_fount_segs(pTHX_ AV *asegs, int *count) {
@@ -1810,10 +1906,11 @@ i_bezier_multi(im,x,y,val)
     i_bezier_multi(im,size_x,x,y,val);
 
 int
-i_poly_aa(im,x,y,val)
+i_poly_aa_m(im,x,y,mode,val)
     Imager::ImgRaw     im
     double *x
     double *y
+    i_poly_fill_mode_t mode
     Imager::Color  val
   PREINIT:
     STRLEN   size_x;
@@ -1821,15 +1918,16 @@ i_poly_aa(im,x,y,val)
   CODE:
     if (size_x != size_y)
       croak("Imager: x and y arrays to i_poly_aa must be equal length\n");
-    RETVAL = i_poly_aa(im, size_x, x, y, val);
+    RETVAL = i_poly_aa_m(im, size_x, x, y, mode, val);
   OUTPUT:
     RETVAL
 
 int
-i_poly_aa_cfill(im, x, y, fill)
+i_poly_aa_cfill_m(im, x, y, mode, fill)
     Imager::ImgRaw     im
     double *x
     double *y
+    i_poly_fill_mode_t mode
     Imager::FillHandle     fill
   PREINIT:
     STRLEN size_x;
@@ -1837,10 +1935,32 @@ i_poly_aa_cfill(im, x, y, fill)
   CODE:
     if (size_x != size_y)
       croak("Imager: x and y arrays to i_poly_aa_cfill must be equal length\n");
-    RETVAL = i_poly_aa_cfill(im, size_x, x, y, fill);
+    RETVAL = i_poly_aa_cfill_m(im, size_x, x, y, mode, fill);
   OUTPUT:
     RETVAL
 
+int
+i_poly_poly_aa(im, polys, mode, color)
+    Imager::ImgRaw     im
+         i_polygon_list polys
+        i_poly_fill_mode_t mode
+        Imager::Color color
+    CODE:
+        RETVAL = i_poly_poly_aa(im, polys.count, polys.polygons, mode, color);
+    OUTPUT:
+        RETVAL
+
+int
+i_poly_poly_aa_cfill(im, polys, mode, fill)
+    Imager::ImgRaw     im
+         i_polygon_list polys
+        i_poly_fill_mode_t mode
+        Imager::FillHandle fill
+    CODE:
+        RETVAL = i_poly_poly_aa_cfill(im, polys.count, polys.polygons, mode, fill);
+    OUTPUT:
+        RETVAL
+
 undef_int
 i_flood_fill(im,seedx,seedy,dcol)
     Imager::ImgRaw     im
index f832eed..c91279a 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -339,6 +339,7 @@ t/250-draw/020-flood.t              Flood fill tests
 t/250-draw/030-paste.t         Test the paste() method
 t/250-draw/040-rubthru.t       Test the rubthrough() method
 t/250-draw/050-polyaa.t                polygon()
+t/250-draw/060-polypoly.t      polypolygon()
 t/250-draw/100-fill.t          fills
 t/250-draw/200-compose.t       compose()
 t/300-transform/010-scale.t    scale(), scaleX() and scaleY()
index 0d42826..2780286 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -158,6 +158,14 @@ extern i_img *i_matrix_transform_bg(i_img *im, i_img_dim xsize, i_img_dim ysize,
 void i_bezier_multi(i_img *im,int l,const double *x,const double *y,const i_color *val);
 int i_poly_aa     (i_img *im,int l,const double *x,const double *y,const i_color *val);
 int i_poly_aa_cfill(i_img *im,int l,const double *x,const double *y,i_fill_t *fill);
+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);
+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);
+extern int
+i_poly_poly_aa(i_img *im, int count, const i_polygon_t *polys,
+              i_poly_fill_mode_t mode, const i_color *val);
+extern int
+i_poly_poly_aa_cfill(i_img *im, int count, const i_polygon_t *polys,
+                    i_poly_fill_mode_t mode, i_fill_t *fill);
 
 undef_int i_flood_fill  (i_img *im,i_img_dim seedx,i_img_dim seedy, const i_color *dcol);
 undef_int i_flood_cfill(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill);
index c684e58..30590ce 100644 (file)
@@ -448,6 +448,11 @@ typedef struct i_polygon_tag {
   size_t count;
 } i_polygon_t;
 
+typedef enum i_poly_fill_mode_tag {
+  i_pfm_evenodd,
+  i_pfm_nonzero
+} i_poly_fill_mode_t;
+
 /* Generic fills */
 struct i_fill_tag;
 
diff --git a/imext.c b/imext.c
index 4471afe..98f2c45 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -204,7 +204,13 @@ im_ext_funcs imager_function_table =
     i_mutex_unlock,
     im_context_slot_new,
     im_context_slot_set,
-    im_context_slot_get
+    im_context_slot_get,
+
+    /* level 9 */
+    i_poly_poly_aa,
+    i_poly_poly_aa_cfill,
+    i_poly_aa_m,
+    i_poly_aa_cfill_m
   };
 
 /* in general these functions aren't called by Imager internally, but
diff --git a/imext.h b/imext.h
index 6e834b2..6561e9a 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -155,6 +155,11 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_flood_fill_border(im, seedx, seedy, dcol, border) ((im_extt->f_i_flood_fill_border)((im), (seedx), (seedy), (dcol), (border)))
 #define i_flood_cfill_border(im, seedx, seedy, fill, border) ((im_extt->f_i_flood_cfill_border)((im), (seedx), (seedy), (fill), (border)))
 
+#define i_poly_aa_m(im, count, x, y, mode, c) ((im_extt->f_i_poly_aa_m)((im), (count), (x), (y), (mode), (c)))
+#define i_poly_aa_cfill_m(im, count, x, y, mode, fill) ((im_extt->f_i_poly_aa_m)((im), (count), (x), (y), (mode), (fill)))
+#define i_poly_poly_aa(im, count, polys, mode, c) ((im_extt->f_i_poly_poly_aa)((im), (count), (polys), (mode), (c)))
+#define i_poly_poly_aa_cfill(im, count, polys, mode, fill) ((im_extt->f_i_poly_poly_aa_cfill)((im), (count), (polys), (mode), (fill)))
+
 #define i_copyto(im, src, x1, y1, x2, y2, tx, ty) \
   ((im_extt->f_i_copyto)((im), (src), (x1), (y1), (x2), (y2), (tx), (ty)))
 #define i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans) \
index b2db7fe..bef5844 100644 (file)
@@ -217,7 +217,7 @@ typedef struct {
   size_t (*f_io_slurp)(i_io_glue_t *ig, unsigned char **c);
   void (*f_io_glue_destroy)(i_io_glue_t *ig);
 
-  /* IMAGER_API_LEVEL 8 functions will be added here */
+  /* IMAGER_API_LEVEL 8 */
   i_img *(*f_im_img_8_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
   i_img *(*f_im_img_16_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
   i_img *(*f_im_img_double_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
@@ -254,6 +254,21 @@ typedef struct {
   im_slot_t (*f_im_context_slot_new)(im_slot_destroy_t);
   int (*f_im_context_slot_set)(im_context_t, im_slot_t, void *);
   void *(*f_im_context_slot_get)(im_context_t, im_slot_t);
+
+  /* IMAGER_API_LEVEL 9 */
+  int (*f_i_poly_poly_aa)(i_img *im, int count, const i_polygon_t *polys,
+                         i_poly_fill_mode_t mode, const i_color *val);
+  int (*f_i_poly_poly_aa_cfill)(i_img *im, int count, const i_polygon_t *polys,
+                               i_poly_fill_mode_t mode, i_fill_t *fill);
+  int (*f_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);
+  int (*f_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);
+
+  /* IMAGER_API_LEVEL 10 functions will be added here */
+
+
 } im_ext_funcs;
 
 #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
index a607dde..11927d5 100644 (file)
@@ -2368,6 +2368,22 @@ will change:
 
 =item *
 
+B<i_poly_aa_cfill_m>
+
+=item *
+
+B<i_poly_aa_m>
+
+=item *
+
+B<i_poly_poly_aa>
+
+=item *
+
+B<i_poly_poly_aa_cfill>
+
+=item *
+
 B<im_lhead>
 
 =item *
index 057a129..5a1497e 100644 (file)
@@ -480,6 +480,10 @@ supplied as a L</"Color Parameters">.
 
 =back
 
+=item polypolygon()
+
+C<FIXME>
+
 =item setpixel()
 
   $img->setpixel(x=>50, y=>70, color=>$color);
index 6ca404c..559c070 100644 (file)
--- a/polygon.c
+++ b/polygon.c
@@ -31,6 +31,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 {
@@ -213,6 +214,7 @@ 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)
@@ -236,6 +238,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)
@@ -438,7 +441,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 */
@@ -542,9 +546,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;
+         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) );
@@ -573,9 +596,20 @@ i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
 
 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);
+}
+
+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
@@ -585,7 +619,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 {
@@ -621,7 +655,7 @@ scanline_flush_render(i_img *im, ss_scanline *ss, int y, void *ctx) {
 
 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;
 
@@ -629,7 +663,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);
@@ -637,6 +672,18 @@ i_poly_poly_aa_cfill(i_img *im, int count, const i_polygon_t *polys,
   return result;
 }
 
+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) {
@@ -646,5 +693,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);
 }
index ace2e64..5992b54 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 
 use strict;
-use Test::More tests => 18;
+use Test::More tests => 24;
 
 use Imager qw/NC/;
 use Imager::Test qw(is_image is_color3);
@@ -10,12 +10,23 @@ sub PI () { 3.14159265358979323846 }
 
 -d "testout" or mkdir "testout";
 
-Imager::init_log("testout/t75aapolyaa.log",1);
+my @out_files;
+
+END {
+  unless ($ENV{IMAGER_KEEP_FILES}) {
+    unlink @out_files;
+    rmdir "testout";
+  }
+}
+
+Imager->open_log(log => "testout/250-polyaa.log");
+push @out_files, "testout/250-polyaa.log";
 
 my $red   = Imager::Color->new(255,0,0);
 my $green = Imager::Color->new(0,255,0);
 my $blue  = Imager::Color->new(0,0,255);
 my $white = Imager::Color->new(255,255,255);
+my $black = Imager::Color->new(0, 0, 0);
 
 { # artifacts with multiple vertical lobes
   # https://rt.cpan.org/Ticket/Display.html?id=43518
@@ -38,7 +49,8 @@ my $white = Imager::Color->new(255,255,255);
   ok($im->polygon(points => \@pts,
                  color => $white),
      "draw with inside point");
-  ok($im->write(file => "testout/t75inside.ppm"), "save to file");
+  ok($im->write(file => "testout/250-poly-inside.ppm"), "save to file");
+  push @out_files, "testout/250-poly-inside.ppm";
   # both scanlines should be the same
   my $line0 = $im->crop(top => 0, height => 1);
   my $line1 = $im->crop(top => 1, height => 1);
@@ -90,14 +102,16 @@ my $white = Imager::Color->new(255,255,255);
   
   
   my ($x, $y) = array_to_refpair(@data);
-  ok(Imager::i_poly_aa($img->{IMG}, $x, $y, $white), "primitive poly");
+  ok(Imager::i_poly_aa_m($img->{IMG}, $x, $y, 0, $white), "primitive poly");
 
-  ok($img->write(file=>"testout/t75.ppm"), "write to file")
+  ok($img->write(file=>"testout/250-poly.ppm"), "write to file")
     or diag $img->errstr;
+  push @out_files, "testout/250-poly.ppm";
 
   my $zoom = make_zoom($img, 8, \@data, $red);
   ok($zoom, "make zoom of primitive");
-  $zoom->write(file=>"testout/t75zoom.ppm") or die $zoom->errstr;
+  $zoom->write(file=>"testout/250-poly-zoom.ppm") or die $zoom->errstr;
+  push @out_files, "testout/250-poly-zoom.ppm";
 }
 
 {
@@ -113,13 +127,14 @@ my $white = Imager::Color->new(255,255,255);
                               )
                        );
     my ($x, $y) = array_to_refpair(@data);
-    Imager::i_poly_aa($img->{IMG}, $x, $y, NC(rand(255), rand(255), rand(255)))
+    Imager::i_poly_aa_m($img->{IMG}, $x, $y, 0, NC(rand(255), rand(255), rand(255)))
        or $good = 0;
   }
   
-  $img->write(file=>"testout/t75big.ppm") or die $img->errstr;
+  $img->write(file=>"testout/250-poly-big.ppm") or die $img->errstr;
 
   ok($good, "primitive squares");
+  push @out_files, "testout/250-poly-big.ppm";
 }
 
 {
@@ -134,7 +149,8 @@ my $white = Imager::Color->new(255,255,255);
                 ), "method call")
     or diag $img->errstr();
 
-  $img->write(file=>"testout/t75wave.ppm") or die $img->errstr;
+  $img->write(file=>"testout/250-poly-wave.ppm") or die $img->errstr;
+  push @out_files, "testout/250-poly-wave.ppm";
 }
 
 {
@@ -152,8 +168,9 @@ my $white = Imager::Color->new(255,255,255);
                 ), "bug check")
     or diag $img->errstr();
 
-  make_zoom($img,20,\@data, $blue)->write(file=>"testout/t75wavebug.ppm") or die $img->errstr;
+  make_zoom($img,20,\@data, $blue)->write(file=>"testout/250-poly-wavebug.ppm") or die $img->errstr;
 
+  push @out_files, "testout/250-poly-wavebug.ppm";
 }
 
 {
@@ -166,7 +183,8 @@ my $white = Imager::Color->new(255,255,255);
                         ],
              ), "poly filled with hatch")
     or diag $img->errstr();
-  $img->write(file=>"testout/t75wave_fill.ppm") or die $img->errstr;
+  $img->write(file=>"testout/250-poly-wave_fill.ppm") or die $img->errstr;
+  push @out_files, "testout/250-poly-wave_fill.ppm";
 }
 
 {
@@ -179,11 +197,61 @@ my $white = Imager::Color->new(255,255,255);
                         ],
              ), "hatched to 16-bit image")
     or diag $img->errstr();
-  $img->write(file=>"testout/t75wave_fill16.ppm") or die $img->errstr;
+  $img->write(file=>"testout/250-poly-wave_fill16.ppm") or die $img->errstr;
+  push @out_files, "testout/250-poly-wave_fill16.ppm";
 }
 
-Imager::malloc_state();
+{
+  my $img = Imager->new(xsize => 100, ysize => 100);
+  my $poly =
+    [
+     [
+      [ 10, 90, 90, 10 ],
+      [ 10, 10, 90, 90 ],
+     ],
+     [
+      [ 20, 45, 45, 20 ],
+      [ 20, 20, 80, 80 ],
+     ],
+     [
+      [ 55, 55, 80, 80 ],
+      [ 20, 80, 80, 20 ],
+     ],
+    ];
+  ok($img->polypolygon
+     (
+      points => $poly,
+      filled => 1,
+      color => $white,
+     ), "default polypolygon");
+  push @out_files, "testout/250-poly-ppeo.ppm";
+  ok($img->write(file => "testout/250-poly-ppeo.ppm"),
+     "save to file");
+  my $cmp_eo = Imager->new(xsize => 100, ysize => 100);
+  $cmp_eo->box(filled => 1, color => $white, box => [ 10, 10, 89, 89 ]);
+  $cmp_eo->box(filled => 1, color => $black, box => [ 20, 20, 44, 79 ]);
+  $cmp_eo->box(filled => 1, color => $black, box => [ 55, 20, 79, 79 ]);
+  is_image($img, $cmp_eo, "check even/odd matches");
+  $img = Imager->new(xsize => 100, ysize => 100);
+  ok($img->polypolygon
+     (
+      points => $poly,
+      filled => 1,
+      color => $white,
+      mode => "nonzero",
+     ), "default polypolygon");
+  my $cmp_nz = Imager->new(xsize => 100, ysize => 100);
+  $cmp_nz->box(filled => 1, color => $white, box => [ 10, 10, 89, 89 ]);
+  $cmp_nz->box(filled => 1, color => $black, box => [ 55, 20, 79, 79 ]);
+  is_image($img, $cmp_nz, "check non-zero matches");
+  push @out_files, "testout/250-poly-ppnz.ppm";
+  ok($img->write(file => "testout/250-poly-ppnz.ppm"),
+     "save to file");
+}
 
+Imager->close_log;
+
+Imager::malloc_state();
 
 #initialized in a BEGIN, later
 my %primitives;
diff --git a/t/250-draw/060-polypoly.t b/t/250-draw/060-polypoly.t
new file mode 100644 (file)
index 0000000..9ef776a
--- /dev/null
@@ -0,0 +1,126 @@
+#!perl -w
+
+use strict;
+use Test::More tests => 12;
+
+use Imager qw/NC/;
+use Imager::Test qw(is_image is_color3);
+
+sub PI () { 3.14159265358979323846 }
+
+-d "testout" or mkdir "testout";
+
+my @cleanup;
+push @cleanup, "testout/060-polypoly.log";
+Imager->open_log(log => "testout/060-polypoly.log");
+
+my $red   = NC(255,0,0);
+my $green = NC(0,255,0);
+my $blue  = NC(0,0,255);
+my $white = NC(255,255,255);
+my $black = NC(0, 0, 0);
+
+END {
+  unlink @cleanup unless $ENV{IMAGER_KEEP_FILES};
+  rmdir "testout";
+}
+
+{
+  my $out = "testout/060-ppsimple.ppm";
+  my $im = Imager->new(xsize => 100, ysize => 100);
+  ok($im->polypolygon
+     (
+      filled => 1,
+      color => $red,
+      points =>
+      [
+       [
+       [ 20, 20, 40, 40 ],
+       [ 20, 80, 80, 20 ],
+       ],
+       [
+       [ 60, 60, 80, 80 ],
+       [ 20, 80, 80, 20 ],
+       ],
+      ]
+     ), "simple filled polypolygon");
+  ok($im->write(file => $out), "save to $out");
+  my $cmp = Imager->new(xsize => 100, ysize => 100);
+  $cmp->box(filled => 1, color => $red, box => [ 20, 20, 39, 79 ]);
+  $cmp->box(filled => 1, color => $red, box => [ 60, 20, 79, 79 ]);
+  is_image($im, $cmp, "check expected output");
+  push @cleanup, $out;
+}
+
+{
+  my $im = Imager->new(xsize => 100, ysize => 100);
+  my $cross_cw =
+    [
+     [
+      [ 10, 90, 90, 10 ],
+      [ 40, 40, 60, 60 ],
+     ],
+     [
+      [ 40, 60, 60, 40 ],
+      [ 10, 10, 90, 90 ],
+     ],
+    ];
+  ok($im->polypolygon
+     (
+      filled => 1,
+      color => $red,
+      points =>$cross_cw,
+      mode => "nonzero",
+     ), "cross polypolygon nz");
+  save($im, "060-ppcrossnz.ppm");
+  my $cmp = Imager->new(xsize => 100, ysize => 100);
+  $cmp->box(filled => 1, color => $red, box => [ 10, 40, 89, 59 ]);
+  $cmp->box(filled => 1, color => $red, box => [ 40, 10, 59, 89 ]);
+  is_image($im, $cmp, "check expected output");
+
+  my $im2 = Imager->new(xsize => 100, ysize => 100);
+  ok($im2->polypolygon
+     (
+      filled => 1,
+      color => $red,
+      points =>$cross_cw,
+      #mode => "nonzero", # default to evenodd
+     ), "cross polypolygon eo");
+  save($im2, "060-ppcrosseo.ppm");
+
+  $cmp->box(filled => 1, color => $black, box => [ 40, 40, 59, 59 ]);
+  is_image($im2, $cmp, "check expected output");
+
+  # same as cross_cw except that the second box is in reversed order
+  my $cross_diff =
+    [
+     [
+      [ 10, 90, 90, 10 ],
+      [ 40, 40, 60, 60 ],
+     ],
+     [
+      [ 40, 60, 60, 40 ],
+      [ 90, 90, 10, 10 ],
+     ],
+    ];
+  my $im3 = Imager->new(xsize => 100, ysize => 100);
+  ok($im3->polypolygon
+     (
+      filled => 1,
+      color => $red,
+      points => $cross_diff,
+      mode => "nonzero",
+     ), "cross polypolygon diff");
+  is_image($im3, $cmp, "check expected output");
+  save($im3, "060-ppcrossdiff.ppm");
+}
+
+Imager->close_log;
+
+sub save {
+  my ($im, $file) = @_;
+
+  $file = "testout/" . $file;
+  push @cleanup, $file;
+  ok($im->write(file => $file), "save to $file");
+}
index 16d231a..c9a1f93 100644 (file)
@@ -158,6 +158,28 @@ Imager do_lots(Imager src) {
   i_box_filled(im, 11, 25, 19, 29, &blue);
   i_flood_cfill_border(im, 15, 25, hatch, &black);
 
+  {
+     double x[3];
+     double y[3];
+     i_polygon_t poly;
+     x[0] = 55;
+     y[0] = 25;
+     x[1] = 55;
+     y[1] = 50;
+     x[2] = 70;
+     y[2] = 50;
+     i_poly_aa_m(im, 3, x, y, i_pfm_evenodd, &red);
+     x[2] = 40;
+     i_poly_aa_cfill_m(im, 3, x, y, i_pfm_evenodd, hatch);
+     y[0] = 65;
+     poly.x = x;
+     poly.y = y;
+     poly.count = 3;
+     i_poly_poly_aa(im, 1, &poly, i_pfm_nonzero, &green);
+     x[2] = 70;
+     i_poly_poly_aa_cfill(im, 1, &poly, i_pfm_nonzero, hatch);
+  }
+
   i_fill_destroy(fount_fill);
   i_fill_destroy(fhatch_fill);
   i_fill_destroy(solid_fill);
@@ -233,7 +255,7 @@ Imager do_lots(Imager src) {
     i_img_destroy(im);
     return NULL;
   }
-    
+
   return im;
 }
 
index bd15d4a..a3ea2d2 100644 (file)
@@ -5,6 +5,7 @@
 i_channel_list         T_IM_CHANNEL_LIST
 i_sample_list          T_IM_SAMPLE_LIST
 i_fsample_list         T_IM_FSAMPLE_LIST
+i_polygon_list         T_IM_POLYGON_LIST
 
 off_t                  T_OFF_T
 
@@ -18,6 +19,7 @@ int *                         T_AVARRAY
 i_img_dim *            T_AVARRAY
 i_color *              T_AVARRAY
 
+i_poly_fill_mode_t     T_I_POLY_FILL_MODE_T
 
 #############################################################################
 INPUT
@@ -109,6 +111,9 @@ T_IM_FSAMPLE_LIST
            croak(\"$pname: no samples provided in $var\");
        }
 
+T_IM_POLYGON_LIST
+        S_get_polygon_list(aTHX_ &$var, $arg);
+
 T_AVARRAY
        STMT_START {
                SV* const xsub_tmp_sv = $arg;
@@ -132,6 +137,11 @@ T_AVARRAY
                }
        } STMT_END
 
+T_I_POLY_FILL_MODE_T
+       $var = S_get_poly_fill_mode(aTHX_ $arg);
+
+
+
 #############################################################################
 OUTPUT