$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
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'});
$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
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
#include "imperl.h"
+#define ARRAY_COUNT(array) (sizeof(array)/sizeof(*array))
+
/*
Context object management
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.
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)
}
}
+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) {
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;
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;
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
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()
i_color color;
color.rgba.r = 255; color.rgba.g = 0; color.rgba.b = 255;
+ double x[] = { ... };
+ double y[] = { ... };
+ i_polygon_t poly;
+ poly.count = sizeof(x) / sizeof(*x);
+ poly.x = x;
+ poly.y = y;
EOS
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);
BOUNDING_BOX_COUNT
};
+typedef struct i_polygon_tag {
+ const double *x;
+ const double *y;
+ 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;
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
#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) \
will result in an increment of IMAGER_API_LEVEL.
*/
-#define IMAGER_API_LEVEL 8
+#define IMAGER_API_LEVEL 9
typedef struct {
int version;
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);
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"
See L</Context objects> for more information.
+=head2 i_polygon_t
+
+Represents a single polygon supplied to i_poly_poly_aa() and
+i_poly_poly_aa_cfill().
+
+This is a structure with 3 members:
+
+=over
+
+=item *
+
+C<x>, C<y> - pointers to the first elements of arrays of doubles that define
+the vertices of the polygon.
+
+=item *
+
+C<count> - the number of values in each of the C<x> and C<y> arrays.
+
+=back
+
+=head2 i_poly_fill_mode_t
+
+An enumerated type of the possible fill modes for polygons:
+
+=over
+
+=item *
+
+C<i_pfm_evenodd> - if areas overlap an odd number of times, they
+are filled, and are otherwise unfilled.
+
+=item *
+
+C<i_pfm_nonzero> - areas that have an unbalanced clockwise and
+anti-clockwise boundary are filled. This is the same as
+C<WindingRule> for X and C<WINDING> for Win32 GDI.
+
+=back
+
=head1 Create an XS module using the Imager API
=head2 Foo.pm
i_color color;
color.rgba.r = 255; color.rgba.g = 0; color.rgba.b = 255;
+ double x[] = { ... };
+ double y[] = { ... };
+ i_polygon_t poly;
+ poly.count = sizeof(x) / sizeof(*x);
+ poly.x = x;
+ poly.y = y;
# Blit tools
i_flood_cfill(im, 50, 50, fill);
i_flood_fill_border(im, 50, 50, &color, &border);
i_flood_cfill_border(im, 50, 50, fill, border);
+ i_poly_poly_aa(im, 1, &poly, mode, color);
+ i_poly_aa_m(im, count, x, y, mode, color);
+ i_poly_poly_aa_cfill(im, 1, &poly, mode, fill);
+ i_poly_aa_cfill(im, count, x, y, mode, fill);
# Error handling
im_clear_error(aIMCTX);
=for comment
From: File imext.c
+=item i_poly_aa_cfill_m(im, count, x, y, mode, fill)
+
+ i_poly_aa_cfill(im, count, x, y, mode, fill);
+
+Fill a polygon defined by the points specified by the x and y arrays with
+the fill specified by C<fill>.
+
+
+=for comment
+From: File polygon.c
+
+=item i_poly_aa_m(im, count, x, y, mode, color)
+
+ i_poly_aa_m(im, count, x, y, mode, color);
+
+Fill a polygon defined by the points specified by the x and y arrays with
+the color specified by C<color>.
+
+
+=for comment
+From: File polygon.c
+
+=item i_poly_poly_aa(im, count, polys, mode, color)
+
+ i_poly_poly_aa(im, 1, &poly, mode, color);
+
+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.
+
+
+=for comment
+From: File polygon.c
+
+=item i_poly_poly_aa_cfill(im, count, polys, mode, fill)
+
+ i_poly_poly_aa_cfill(im, 1, &poly, mode, fill);
+
+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.
+
+
+=for comment
+From: File polygon.c
+
=item i_ppal(im, left, right, y, indexes)
hatched fills, image based fills and fountain fills. See
L<Imager::Fill> for more information.
+=head2 Polygon Fill Modes
+
+When filling a polygon that overlaps itself, or when filling several
+polygons with polypolygon() that overlap each other, you can supply a
+C<mode> parameter that controls how the overlap is resolved. This can
+have one of two possible values:
+
+=over
+
+=item *
+
+C<evenodd> - if areas overlap an odd number of times, they are filled,
+and are otherwise unfilled. This is the default and the historical
+Imager polygon fill mode.
+
+=item *
+
+C<nonzero> - areas that have an unbalanced clockwise and
+anti-clockwise boundary are filled. This is the same as
+C<WindingRule> for X and C<WINDING> for Win32 GDI.
+
+=back
+
+C<nonzero> allows polygons to overlap, either with itself, or with
+another polygon in the same polypolygon() call, without producing
+unfilled area in the overlap, and also allows areas to be cut out of
+the area by specifying the points making up a cut-out in the opposite
+order.
+
=head2 List of primitives
=over
C<fill> - the fill for the filled circle. See L</"Fill Parameters">
+=item *
+
+C<mode> - fill mode for the polygon. See L</"Polygon Fill Modes">
+
+=back
+
+Note: the points specified are as offsets from the top-left of the
+image, I<not> as pixel locations. This means that:
+
+ $img->polygon(points => [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ]);
+
+fills only a single pixel at C<(0, 0)>, not four.
+
+=item polypolygon()
+X<polypolygon() method>X<methods, polypolygon>
+
+ $img->polypolygon(points => $points, color => $color);
+
+Draw multiple polygons, either filled or unfilled.
+
+=over
+
+=item *
+
+C<points> - is an array reference containing polygon definitions, each
+polygon definition is a reference to an array containing two arrays,
+one each for the C<x> and C<y> co-ordinates.
+
+=item *
+
+C<filled> - if true, fill the polygons with the color defined by
+C<color>.
+
+=item *
+
+C<color> - the color to draw the polygons with if C<fill> is not
+supplied.
+
+=item *
+
+C<fill> - fill the polygons with this fill if supplied.
+
+=item *
+
+C<mode> - fill mode for the polygon. See L</"Polygon Fill Modes">
+
=back
+Note: the points specified are as offsets from the top-left of the
+image, I<not> as pixel locations. This means that:
+
+ $img->polypolygon(points => [ [ [ 0, 1, 1, 0 ], [ 0, 0, 1, 1 ] ] ],
+ filled => 1);
+
+fills only a single pixel at C<(0, 0)>, not four.
+
=item flood_fill()
X<flood_fill>You can fill a region that all has the same color using
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "draw.h"
#include "log.h"
typedef i_img_dim pcord;
typedef struct {
- int n;
+ size_t n;
pcord x,y;
} p_point;
typedef struct {
- int n;
+ size_t n;
pcord x1,y1;
pcord x2,y2;
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 {
- int n;
+ size_t n;
double x;
} p_slice;
static
p_line *
-line_set_new(const double *x, const double *y, int l) {
- int i;
- p_line *lset = mymalloc(sizeof(p_line) * l);
-
- for(i=0; i<l; i++) {
- lset[i].n=i;
- lset[i].x1 = IMTRUNC(x[i]);
- 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=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);
+line_set_new(const i_polygon_t *polys, size_t count, size_t *line_count) {
+ size_t i, j, n;
+ p_line *lset, *line;
+ size_t lines = 0;
+
+ for (i = 0; i < count; ++i)
+ lines += polys[i].count;
+
+ line = lset = mymalloc(sizeof(p_line) * lines);
+
+ n = 0;
+ for (i = 0; i < count; ++i) {
+ const i_polygon_t *p = polys + i;
+
+ for(j = 0; j < p->count; j++) {
+ 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;
}
static
p_point *
-point_set_new(const double *x, const double *y, int l) {
- int i;
- p_point *pset = mymalloc(sizeof(p_point) * l);
+point_set_new(const i_polygon_t *polys, size_t count, size_t *point_count) {
+ size_t i, j, n;
+ p_point *pset, *pt;
+ size_t points = 0;
- for(i=0; i<l; i++) {
- pset[i].n=i;
- pset[i].x=IMTRUNC(x[i]);
- pset[i].y=IMTRUNC(y[i]);
+ for (i = 0; i < count; ++i)
+ points += polys[i].count;
+
+ *point_count = points;
+
+ pt = pset = mymalloc(sizeof(p_point) * points);
+
+ n = 0;
+ for (i = 0; i < count; ++i) {
+ const i_polygon_t *p = polys + i;
+
+ for(j = 0; j < p->count; j++) {
+ pt->n = n++;
+ pt->x = IMTRUNC(p->x[j]);
+ pt->y = IMTRUNC(p->y[j]);
+ ++pt;
+ }
}
return pset;
}
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);
}
(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);
}
(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)
}
}
-
-
static
unsigned char
saturate(int in) {
*/
-static void
-i_poly_aa_low(i_img *im, int l, const double *x, const double *y, void *ctx, scanline_flusher flusher) {
+static int
+i_poly_poly_aa_low(i_img *im, int count, const i_polygon_t *polys,
+ 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 */
p_point *pset; /* List of points in polygon */
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();
+
+ if (count < 1) {
+ i_push_error(0, "no polygons to draw");
+ return 0;
+ }
- 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 (k = 0; k < count; ++k) {
+ if (polys[k].count < 3) {
+ i_push_errorf(0, "polygons must have at least 3 points");
+ return 0;
+ }
+ }
- for(i=0; i<l; i++) {
- mm_log((2, "(%.2f, %.2f)\n", x[i], y[i]));
+ for (k = 0; k < count; ++k) {
+ const i_polygon_t *p = polys + k;
+ im_log((aIMCTX, 2, "poly %d\n", k));
+ for(i = 0; i < p->count; i++) {
+ im_log((aIMCTX, 2, " (%.2f, %.2f)\n", p->x[i], p->y[i]));
+ }
}
setbuf(stdout, NULL);
);
- tllist = mymalloc(sizeof(p_slice)*l);
-
- ss_scanline_init(&templine, im->xsize, l);
-
- pset = point_set_new(x, y, l);
- lset = line_set_new(x, y, l);
+ pset = point_set_new(polys, count, &pcount);
+ lset = line_set_new(polys, count, &lcount);
+ ss_scanline_init(&templine, im->xsize, lcount);
- qsort(pset, l, sizeof(p_point), (int(*)(const void *,const void *))p_compy);
+ tllist = mymalloc(sizeof(p_slice) * lcount);
+
+ qsort(pset, pcount, sizeof(p_point), (int(*)(const void *,const void *))p_compy);
POLY_DEB(
- for(i=0;i<l;i++) {
+ for(i=0;i<lcount;i++) {
printf("%d [ %d ] (%d , %d) -> (%d , %d) yspan ( %d , %d )\n",
i, lset[i].n, lset[i].x1, lset[i].y1, lset[i].x2, lset[i].y2, lset[i].miny, lset[i].maxy);
}
/* loop on intervals */
- for(i=0; i<l-1; i++) {
+ for(i=0; i<pcount-1; i++) {
i_img_dim startscan = i_max( coarse(pset[i].y), 0);
i_img_dim stopscan = i_min( coarse(pset[i+1].y+15), im->ysize);
pcord miny, maxy; /* y bounds in fine coordinates */
continue;
}
- clc = lines_in_interval(lset, l, tllist, pset[i].y, pset[i+1].y);
+ 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
(
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) );
myfree(pset);
myfree(lset);
myfree(tllist);
-
-} /* Function */
+
+ 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_aa(i_img *im, int l, const double *x, const double *y, const i_color *val) {
+i_poly_poly_aa(i_img *im, int count, const i_polygon_t *polys,
+ i_poly_fill_mode_t mode, const i_color *val) {
i_color c = *val;
- i_poly_aa_low(im, l, x, y, &c, scanline_flush);
- return 1;
+ 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
+i_poly_aa(i_img *im, int l, const double *x, const double *y, 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, i_pfm_evenodd, val);
}
struct poly_render_state {
}
}
+/*
+=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_aa_cfill(i_img *im, int l, const double *x, const double *y,
- i_fill_t *fill) {
+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) {
struct poly_render_state ctx;
+ int result;
i_render_init(&ctx.render, im, im->xsize);
ctx.fill = fill;
ctx.cover = mymalloc(im->xsize);
- i_poly_aa_low(im, l, x, y, &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);
- return 1;
+
+ 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) {
+ i_polygon_t poly;
+
+ poly.count = l;
+ poly.x = x;
+ poly.y = y;
+
+ return i_poly_poly_aa_cfill(im, 1, &poly, i_pfm_evenodd, fill);
}
#!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);
-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
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);
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";
}
{
)
);
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";
}
{
), "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";
}
{
), "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";
}
{
],
), "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";
}
{
],
), "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;
--- /dev/null
+#!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");
+}
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);
i_img_destroy(im);
return NULL;
}
-
+
return im;
}
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
i_img_dim * T_AVARRAY
i_color * T_AVARRAY
+i_poly_fill_mode_t T_I_POLY_FILL_MODE_T
#############################################################################
INPUT
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;
}
} STMT_END
+T_I_POLY_FILL_MODE_T
+ $var = S_get_poly_fill_mode(aTHX_ $arg);
+
+
+
#############################################################################
OUTPUT
CMYK
CPAN
FreeType
+GDI
GIF
HSV
Hrafnkelsson