From 0d80f37ea0e9d76ef46fbd6e4f54c82c8a163b51 Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Wed, 23 Apr 2014 22:34:44 +1000 Subject: [PATCH] access to poly_poly from perl as polypolygon() --- Imager.pm | 71 ++++++++++++++++++++- Imager.xs | 130 ++++++++++++++++++++++++++++++++++++-- MANIFEST | 1 + imager.h | 8 +++ imdatatypes.h | 5 ++ imext.c | 8 ++- imext.h | 5 ++ imexttypes.h | 17 ++++- lib/Imager/APIRef.pod | 16 +++++ lib/Imager/Draw.pod | 4 ++ polygon.c | 67 +++++++++++++++++--- t/250-draw/050-polyaa.t | 94 +++++++++++++++++++++++---- t/250-draw/060-polypoly.t | 126 ++++++++++++++++++++++++++++++++++++ t/450-api/100-inline.t | 24 ++++++- typemap.local | 10 +++ 15 files changed, 552 insertions(+), 34 deletions(-) create mode 100644 t/250-draw/060-polypoly.t diff --git a/Imager.pm b/Imager.pm index 4ff2d740..4db21396 100644 --- 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 polyline() - L +polypolygon() - L + preload() - L read() - L - read a single image from an image file diff --git a/Imager.xs b/Imager.xs index 6847b02b..5fe6b4dc 100644 --- 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 diff --git a/MANIFEST b/MANIFEST index f832eedb..c91279a2 100644 --- 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() diff --git a/imager.h b/imager.h index 0d42826d..27802866 100644 --- 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); diff --git a/imdatatypes.h b/imdatatypes.h index c684e58c..30590ce9 100644 --- a/imdatatypes.h +++ b/imdatatypes.h @@ -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 4471afe9..98f2c457 100644 --- 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 6e834b25..6561e9a8 100644 --- 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) \ diff --git a/imexttypes.h b/imexttypes.h index b2db7fec..bef58446 100644 --- a/imexttypes.h +++ b/imexttypes.h @@ -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" diff --git a/lib/Imager/APIRef.pod b/lib/Imager/APIRef.pod index a607dde0..11927d56 100644 --- a/lib/Imager/APIRef.pod +++ b/lib/Imager/APIRef.pod @@ -2368,6 +2368,22 @@ will change: =item * +B + +=item * + +B + +=item * + +B + +=item * + +B + +=item * + B =item * diff --git a/lib/Imager/Draw.pod b/lib/Imager/Draw.pod index 057a129c..5a1497ef 100644 --- a/lib/Imager/Draw.pod +++ b/lib/Imager/Draw.pod @@ -480,6 +480,10 @@ supplied as a L. =back +=item polypolygon() + +C + =item setpixel() $img->setpixel(x=>50, y=>70, color=>$color); diff --git a/polygon.c b/polygon.c index 6ca404ce..559c0709 100644 --- 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; kdir; + + 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); } diff --git a/t/250-draw/050-polyaa.t b/t/250-draw/050-polyaa.t index ace2e64b..5992b547 100644 --- a/t/250-draw/050-polyaa.t +++ b/t/250-draw/050-polyaa.t @@ -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 index 00000000..9ef776a2 --- /dev/null +++ b/t/250-draw/060-polypoly.t @@ -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"); +} diff --git a/t/450-api/100-inline.t b/t/450-api/100-inline.t index 16d231a6..c9a1f939 100644 --- a/t/450-api/100-inline.t +++ b/t/450-api/100-inline.t @@ -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; } diff --git a/typemap.local b/typemap.local index bd15d4aa..a3ea2d29 100644 --- a/typemap.local +++ b/typemap.local @@ -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 -- 2.30.2