- fixes to verbose mode in Makefile.PL, also added a -v switch so you
can enable it via the command-line
Resolves: http://rt.cpan.org/NoAuth/Bug.html?id=16094
+- arc(..., fill=> ...) wasn't handling concave areas correctly
+- arc(..., color=>...) wasn't properly filling it's area
+- added experimental antialiased support to arc()
=================================================================
return $self;
}
-# Draws an arc - this routine SUCKS and is buggy - it sometimes doesn't work when the arc is a convex polygon
-
sub arc {
my $self=shift;
unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
'x'=>$self->getwidth()/2,
'y'=>$self->getheight()/2,
'd1'=>0, 'd2'=>361, @_);
- if ($opts{fill}) {
- unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
- # assume it's a hash ref
- require 'Imager/Fill.pm';
- unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
- $self->{ERRSTR} = $Imager::ERRSTR;
- return;
+ if ($opts{aa}) {
+ if ($opts{fill}) {
+ unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+ # assume it's a hash ref
+ require 'Imager/Fill.pm';
+ unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ }
+ i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
+ $opts{'d2'}, $opts{fill}{fill});
+ }
+ else {
+ my $color = _color($opts{'color'});
+ unless ($color) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
+ i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
+ $color);
+ }
+ else {
+ i_arc_aa($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
+ $opts{'d1'}, $opts{'d2'}, $color);
}
}
- i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
- $opts{'d2'}, $opts{fill}{fill});
}
else {
- my $color = _color($opts{'color'});
- unless ($color) {
- $self->{ERRSTR} = $Imager::ERRSTR;
- return;
- }
- if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
- i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
- $color);
+ if ($opts{fill}) {
+ unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+ # assume it's a hash ref
+ require 'Imager/Fill.pm';
+ unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ }
+ i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
+ $opts{'d2'}, $opts{fill}{fill});
}
else {
- if ($opts{'d1'} <= $opts{'d2'}) {
- i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
- $opts{'d1'}, $opts{'d2'}, $color);
+ my $color = _color($opts{'color'});
+ unless ($color) {
+ $self->{ERRSTR} = $Imager::ERRSTR;
+ return;
+ }
+ if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
+ i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
+ $color);
}
else {
- i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
- $opts{'d1'}, 361, $color);
- i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
- 0, $opts{'d2'}, $color);
+ i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
+ $opts{'d1'}, $opts{'d2'}, $color);
}
}
}
boxes, drawing - L<Imager::Draw/box>
+changes between image - L<Imager::Filter/"Image Difference">
+
color - L<Imager::Color>
color names - L<Imager::Color>, L<Imager::Color::Table>
combine modes - L<Imager::Fill/combine>
+compare images - L<Imager::Filter/"Image Difference">
+
contrast - L<Imager::Filter/contrast>, L<Imager::Filter/autolevels>
convolution - L<Imager::Filter/conv>
cropping - L<Imager::Transformations/crop>
+C<diff> images - L<Imager::Filter/"Image Difference">
+
dpi - L<Imager::ImageTypes/i_xres>
drawing boxes - L<Imager::Draw/box>
#endif
+#define i_int_hlines_testing() 1
+
#include "image.h"
#include "feat.h"
#include "dynaload.h"
#include "regmach.h"
+#if i_int_hlines_testing()
+#include "imagei.h"
+#endif
+
typedef io_glue* Imager__IO;
typedef i_color* Imager__Color;
typedef i_fcolor* Imager__Color__Float;
potential naming conflicts */
#define init_log m_init_log
+#if i_int_hlines_testing()
+
+typedef i_int_hlines *Imager__Internal__Hlines;
+
+static i_int_hlines *
+i_int_hlines_new(int start_y, int count_y, int start_x, int count_x) {
+ i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
+ i_int_init_hlines(result, start_y, count_y, start_x, count_x);
+
+ return result;
+}
+
+static i_int_hlines *
+i_int_hlines_new_img(i_img *im) {
+ i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
+ i_int_init_hlines_img(result, im);
+
+ return result;
+}
+
+static void
+i_int_hlines_DESTROY(i_int_hlines *hlines) {
+ i_int_hlines_destroy(hlines);
+ myfree(hlines);
+}
+
+static int seg_compare(const void *vleft, const void *vright) {
+ const i_int_hline_seg *left = vleft;
+ const i_int_hline_seg *right = vright;
+
+ return left->minx - right->minx;
+}
+
+static SV *
+i_int_hlines_dump(i_int_hlines *hlines) {
+ SV *dump = newSVpvf("start_y: %d limit_y: %d start_x: %d limit_x: %d\n",
+ hlines->start_y, hlines->limit_y, hlines->start_x, hlines->limit_x);
+ int y;
+
+ for (y = hlines->start_y; y < hlines->limit_y; ++y) {
+ i_int_hline_entry *entry = hlines->entries[y-hlines->start_y];
+ if (entry) {
+ int i;
+ /* sort the segments, if any */
+ if (entry->count)
+ qsort(entry->segs, entry->count, sizeof(i_int_hline_seg), seg_compare);
+
+ sv_catpvf(dump, " %d (%d):", y, entry->count);
+ for (i = 0; i < entry->count; ++i) {
+ sv_catpvf(dump, " [%d, %d)", entry->segs[i].minx,
+ entry->segs[i].x_limit);
+ }
+ sv_catpv(dump, "\n");
+ }
+ }
+
+ return dump;
+}
+
+#endif
+
MODULE = Imager PACKAGE = Imager::Color PREFIX = ICL_
Imager::Color
float d2
Imager::Color val
+void
+i_arc_aa(im,x,y,rad,d1,d2,val)
+ Imager::ImgRaw im
+ double x
+ double y
+ double rad
+ double d1
+ double d2
+ Imager::Color val
+
void
i_arc_cfill(im,x,y,rad,d1,d2,fill)
Imager::ImgRaw im
float d2
Imager::FillHandle fill
+void
+i_arc_aa_cfill(im,x,y,rad,d1,d2,fill)
+ Imager::ImgRaw im
+ double x
+ double y
+ double rad
+ double d1
+ double d2
+ Imager::FillHandle fill
void
OUTPUT:
RETVAL
+MODULE = Imager PACKAGE = Imager::Internal::Hlines PREFIX=i_int_hlines_
+
+# this class is only exposed for testing
+
+int
+i_int_hlines_testing()
+
+#if i_int_hlines_testing()
+
+Imager::Internal::Hlines
+i_int_hlines_new(start_y, count_y, start_x, count_x)
+ int start_y
+ int count_y
+ int start_x
+ int count_x
+
+Imager::Internal::Hlines
+i_int_hlines_new_img(im)
+ Imager::ImgRaw im
+
+void
+i_int_hlines_add(hlines, y, minx, width)
+ Imager::Internal::Hlines hlines
+ int y
+ int minx
+ int width
+
+void
+i_int_hlines_DESTROY(hlines)
+ Imager::Internal::Hlines hlines
+
+SV *
+i_int_hlines_dump(hlines)
+ Imager::Internal::Hlines hlines
+
+#endif
freetyp2.c Implements freetype2 font support
gaussian.c
gif.c
+hlines.c Manage sets of horizontal line segments
image.c
image.h
imagei.h
t/t70newgif.t
t/t75polyaa.t
t/t80texttools.t Test text wrapping
+t/t81hlines.t Test hlines.c
t/t90cc.t
t/t91pod.t Test POD with Test::Pod
t/testtools.pl
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img16.o rotate.o
- bmp.o tga.o rgb.o color.o fills.o imgdouble.o limits.o);
+ bmp.o tga.o rgb.o color.o fills.o imgdouble.o limits.o hlines.o);
%opts=(
'NAME' => 'Imager',
#include "image.h"
#include "draw.h"
#include "log.h"
+#include "imagei.h"
#include <limits.h>
if (ar->data[i].max!=-1) printf("line %d: min=%d, max=%d.\n",i,ar->data[i].min,ar->data[i].max);
}
-
-void
-i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val) {
+static void
+i_arc_minmax(i_int_hlines *hlines,int x,int y,float rad,float d1,float d2) {
i_mmarray dot;
float f,fx,fy;
int x1,y1;
- mm_log((1,"i_arc(im* 0x%x,x %d,y %d,rad %.2f,d1 %.2f,d2 %.2f,val 0x%x)\n",im,x,y,rad,d1,d2,val));
+ /*mm_log((1,"i_arc(im* 0x%x,x %d,y %d,rad %.2f,d1 %.2f,d2 %.2f,val 0x%x)\n",im,x,y,rad,d1,d2,val));*/
- i_mmarray_cr(&dot,im->ysize);
+ i_mmarray_cr(&dot, hlines->limit_y);
x1=(int)(x+0.5+rad*cos(d1*PI/180.0));
y1=(int)(y+0.5+rad*sin(d1*PI/180.0));
/* printf("x1: %d.\ny1: %d.\n",x1,y1); */
i_arcdraw(x, y, x1, y1, &dot);
+ /* render the minmax values onto the hlines */
+ for (y = 0; y < dot.lines; y++) {
+ if (dot.data[y].max!=-1) {
+ int minx, width;
+ minx = dot.data[y].min;
+ width = dot.data[y].max - dot.data[y].min + 1;
+ i_int_hlines_add(hlines, y, minx, width);
+ }
+ }
+
/* dot.info(); */
- i_mmarray_render(im,&dot,val);
i_mmarray_dst(&dot);
}
+static void
+i_arc_hlines(i_int_hlines *hlines,int x,int y,float rad,float d1,float d2) {
+ if (d1 <= d2) {
+ i_arc_minmax(hlines, x, y, rad, d1, d2);
+ }
+ else {
+ i_arc_minmax(hlines, x, y, rad, d1, 360);
+ i_arc_minmax(hlines, x, y, rad, 0, d2);
+ }
+}
+
+void
+i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val) {
+ i_int_hlines hlines;
+
+ i_int_init_hlines_img(&hlines, im);
+
+ i_arc_hlines(&hlines, x, y, rad, d1, d2);
+
+ i_int_hlines_fill_color(im, &hlines, val);
+
+ i_int_hlines_destroy(&hlines);
+}
+
+#define MIN_CIRCLE_STEPS 8
+#define MAX_CIRCLE_STEPS 360
+
void
i_arc_cfill(i_img *im,int x,int y,float rad,float d1,float d2,i_fill_t *fill) {
- i_mmarray dot;
- float f,fx,fy;
- int x1,y1;
+ i_int_hlines hlines;
- mm_log((1,"i_arc_cfill(im* 0x%x,x %d,y %d,rad %.2f,d1 %.2f,d2 %.2f,fill 0x%x)\n",im,x,y,rad,d1,d2,fill));
+ i_int_init_hlines_img(&hlines, im);
- i_mmarray_cr(&dot,im->ysize);
+ i_arc_hlines(&hlines, x, y, rad, d1, d2);
- x1=(int)(x+0.5+rad*cos(d1*PI/180.0));
- y1=(int)(y+0.5+rad*sin(d1*PI/180.0));
- fx=(float)x1; fy=(float)y1;
+ i_int_hlines_fill_fill(im, &hlines, fill);
- /* printf("x1: %d.\ny1: %d.\n",x1,y1); */
- i_arcdraw(x, y, x1, y1, &dot);
+ i_int_hlines_destroy(&hlines);
+}
- x1=(int)(x+0.5+rad*cos(d2*PI/180.0));
- y1=(int)(y+0.5+rad*sin(d2*PI/180.0));
+static void
+arc_poly(int *count, double **xvals, double **yvals,
+ double x, double y, double rad, double d1, double d2) {
+ double d1_rad, d2_rad;
+ double circum;
+ int steps, point_count;
+ double angle_inc;
+
+ /* normalize the angles */
+ d1 = fmod(d1, 360);
+ if (d1 == 0) {
+ if (d2 >= 360) { /* default is 361 */
+ d2 = 360;
+ }
+ else {
+ d2 = fmod(d2, 360);
+ if (d2 < d1)
+ d2 += 360;
+ }
+ }
+ else {
+ d2 = fmod(d2, 360);
+ if (d2 < d1)
+ d2 += 360;
+ }
+ d1_rad = d1 * PI / 180;
+ d2_rad = d2 * PI / 180;
- for(f=d1;f<=d2;f+=0.01) i_mmarray_add(&dot,(int)(x+0.5+rad*cos(f*PI/180.0)),(int)(y+0.5+rad*sin(f*PI/180.0)));
-
- /* printf("x1: %d.\ny1: %d.\n",x1,y1); */
- i_arcdraw(x, y, x1, y1, &dot);
+ /* how many segments for the curved part?
+ we do a maximum of one per degree, with a minimum of 8/circle
+ we try to aim at having about one segment per 2 pixels
+ Work it out per circle to get a step size.
- /* dot.info(); */
- i_mmarray_render_fill(im,&dot,fill);
- i_mmarray_dst(&dot);
+ I was originally making steps = circum/2 but that looked horrible.
+
+ I think there might be an issue in the polygon filler.
+ */
+ circum = 2 * PI * rad;
+ steps = circum;
+ if (steps > MAX_CIRCLE_STEPS)
+ steps = MAX_CIRCLE_STEPS;
+ else if (steps < MIN_CIRCLE_STEPS)
+ steps = MIN_CIRCLE_STEPS;
+
+ angle_inc = 2 * PI / steps;
+
+ point_count = steps + 5; /* rough */
+ *xvals = mymalloc(point_count * sizeof(double));
+ *yvals = mymalloc(point_count * sizeof(double));
+
+ /* from centre to edge at d1 */
+ (*xvals)[0] = x;
+ (*yvals)[0] = y;
+ (*xvals)[1] = x + rad * cos(d1_rad);
+ (*yvals)[1] = y + rad * sin(d1_rad);
+ *count = 2;
+
+ /* step around the curve */
+ while (d1_rad < d2_rad) {
+ (*xvals)[*count] = x + rad * cos(d1_rad);
+ (*yvals)[*count] = y + rad * sin(d1_rad);
+ ++*count;
+ d1_rad += angle_inc;
+ }
+
+ /* finish off the curve */
+ (*xvals)[*count] = x + rad * cos(d2_rad);
+ (*yvals)[*count] = y + rad * sin(d2_rad);
+ ++*count;
+}
+
+void
+i_arc_aa(i_img *im, double x, double y, double rad, double d1, double d2,
+ i_color *val) {
+ double *xvals, *yvals;
+ int count;
+
+ arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
+
+ i_poly_aa(im, count, xvals, yvals, val);
+
+ myfree(xvals);
+ myfree(yvals);
}
+void
+i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
+ i_fill_t *fill) {
+ double *xvals, *yvals;
+ int count;
+ arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
+
+ i_poly_aa_cfill(im, count, xvals, yvals, fill);
+
+ myfree(xvals);
+ myfree(yvals);
+}
/* Temporary AA HACK */
void i_line (i_img *im,int x1,int y1,int x2,int y2,i_color *val, int endp);
void i_line_aa (i_img *im,int x1,int y1,int x2,int y2,i_color *val, int endp);
void i_arc (i_img *im,int x,int y,float rad,float d1,float d2,i_color *val);
+void i_arc_aa (i_img *im, double x, double y, double rad, double d1, double d2,i_color *val);
void i_arc_cfill(i_img *im,int x,int y,float rad,float d1,float d2,i_fill_t *fill);
+void i_arc_aa_cfill(i_img *im,double x,double y,double rad,double d1,double d2,i_fill_t *fill);
void i_circle_aa (i_img *im,float x, float y,float rad,i_color *val);
void i_copyto (i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty);
void i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans);
extern int
i_int_check_image_file_limits(int width, int height, int channels, int sample_size);
+#define im_min(a, b) ((a) < (b) ? (a) : (b))
+#define im_max(a, b) ((a) > (b) ? (a) : (b))
+
#include "ext.h"
extern UTIL_table_t i_UTIL_table;
/* test if all channels are writable */
#define I_ALL_CHANNELS_WRITABLE(im) (((im)->ch_mask & 0xF) == 0xf)
+typedef struct i_int_hline_seg_tag {
+ int minx, x_limit;
+} i_int_hline_seg;
+
+typedef struct i_int_hline_entry_tag {
+ int count;
+ int alloc;
+ i_int_hline_seg segs[1];
+} i_int_hline_entry;
+
+/* represents a set of horizontal line segments to be filled in later */
+typedef struct i_int_hlines_tag {
+ int start_y, limit_y;
+ int start_x, limit_x;
+ i_int_hline_entry **entries;
+} i_int_hlines;
+
+extern void
+i_int_init_hlines(
+ i_int_hlines *hlines,
+ int start_y,
+ int count_y,
+ int start_x,
+ int width_x
+ );
+extern void i_int_init_hlines_img(i_int_hlines *hlines, i_img *img);
+extern void i_int_hlines_add(i_int_hlines *hlines, int y, int minx, int width);
+extern void i_int_hlines_destroy(i_int_hlines *hlines);
+
+extern void i_int_hlines_fill_color(i_img *im, i_int_hlines *hlines, i_color *val);
+extern void i_int_hlines_fill_fill(i_img *im, i_int_hlines *hlines, i_fill_t *fill);
+
#endif
+#!perl -w
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'
# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)
use strict;
+use Test::More tests => 38;
my $loaded;
-BEGIN { $| = 1; print "1..29\n"; }
-END {print "not ok 1\n" unless $loaded;}
-use Imager qw/:all/;
-$loaded = 1;
-print "ok 1\n";
-
+BEGIN { use_ok(Imager=>':all'); }
init_log("testout/t21draw.log",1);
my $redobj = NC(255, 0, 0);
my $blue = { hue=>240, saturation=>1, value=>1 };
my $white = '#FFFFFF';
-my $testnum = 2;
-
-my $img = Imager->new(xsize=>100, ysize=>100);
+my $img = Imager->new(xsize=>100, ysize=>300);
ok($img->box(color=>$blueobj, xmin=>10, ymin=>10, xmax=>48, ymax=>18),
"box with color obj");
"line with colorobj");
# FIXME - neither the start nor end-point is set for a non-aa line
-#my $c = Imager::i_get_pixel($img->{IMG}, 5, 55);
-#ok(color_cmp($c, $blueobj) == 0, "# TODO start point not set");
+my $c = Imager::i_get_pixel($img->{IMG}, 5, 55);
+ok(color_cmp($c, $blueobj) == 0, "# TODO start point not set");
ok($img->line(color=>$red, x1=>10, y1=>55, x2=>40, y2=>95, aa=>1),
"aa line with color");
ok($gp->isa('Imager::Color::Float'), "check scalar float getpixel type");
ok(color_cmp($gp, $flgreen) == 0, "check scalar float getpixel color");
+# more complete arc tests
+ok($img->arc(x=>25, 'y'=>125, r=>20, d1=>315, d2=>45, color=>$greenobj),
+ "color arc through angle 0");
+# use diff combine here to make sure double writing is noticable
+ok($img->arc(x=>75, 'y'=>125, r=>20, d1=>315, d2=>45,
+ fill => { solid=>$blueobj, combine => 'diff' }),
+ "fill arc through angle 0");
+ok($img->arc(x=>25, 'y'=>175, r=>20, d1=>315, d2=>225, color=>$redobj),
+ "concave color arc");
+ok($img->arc(x=>75, 'y'=>175, r=>20, d1=>315, d2=>225,
+ fill => { solid=>$greenobj, combine=>'diff' }),
+ "concave fill arc");
+ok($img->arc(x=>25, y=>225, r=>20, d1=>135, d2=>45, color=>$redobj),
+ "another concave color arc");
+ok($img->arc(x=>75, y=>225, r=>20, d1=>135, d2=>45,
+ fille => { solid=>$blueobj, combine=>'diff' }),
+ "another concave fillarc");
+ok($img->arc(x=>25, y=>275, r=>20, d1=>135, d2=>45, color=>$redobj, aa=>1),
+ "concave color arc aa");
+ok($img->arc(x=>75, y=>275, r=>20, d1=>135, d2=>45,
+ fille => { solid=>$blueobj, combine=>'diff' }, aa=>1),
+ "concave fill arc aa");
+
ok($img->write(file=>'testout/t21draw.ppm'),
"saving output");
malloc_state();
-sub ok {
- my ($ok, $msg) = @_;
-
- if ($ok) {
- print "ok ",$testnum++,"\n";
- }
- else {
- print "not ok ",$testnum++," # $msg\n";
- }
-}
-
sub color_cmp {
my ($l, $r) = @_;
my @l = $l->rgba;
--- /dev/null
+#!perl -w
+use strict;
+use Test::More;
+use Imager;
+
+# this script tests an internal set of functions for Imager, they
+# aren't intended to be used at the perl level.
+# these functions aren't present in all Imager builds
+
+unless (Imager::Internal::Hlines::testing()) {
+ plan skip_all => 'Imager not built to run this test';
+}
+
+plan tests => 15;
+
+my $hline = Imager::Internal::Hlines::new(0, 100, 0, 100);
+my $base_text = 'start_y: 0 limit_y: 100 start_x: 0 limit_x: 100';
+ok($hline, "made hline");
+is($hline->dump, "$base_text\n", "check values");
+$hline->add(5, -5, 7);
+is($hline->dump, <<EOS, "check (-5, 7) added");
+$base_text
+ 5 (1): [0, 2)
+EOS
+$hline->add(5, 8, 4);
+is($hline->dump, <<EOS, "check (8, 4) added");
+$base_text
+ 5 (2): [0, 2) [8, 12)
+EOS
+$hline->add(5, 3, 3);
+is($hline->dump, <<EOS, "check (3, 3) added");
+$base_text
+ 5 (3): [0, 2) [3, 6) [8, 12)
+EOS
+$hline->add(5, 2, 6);
+is($hline->dump, <<EOS, "check (2, 6) added");
+$base_text
+ 5 (1): [0, 12)
+EOS
+# adding out of range should do nothing
+my $current = <<EOS;
+$base_text
+ 5 (1): [0, 12)
+EOS
+$hline->add(6, -5, 5);
+is($hline->dump, $current, "check (6, -5, 5) not added");
+$hline->add(6, 100, 5);
+is($hline->dump, $current, "check (6, 100, 5) not added");
+$hline->add(-1, 5, 2);
+is($hline->dump, $current, "check (-1, 5, 2) not added");
+$hline->add(100, 5, 2);
+is($hline->dump, $current, "check (10, 5, 2) not added");
+
+# overlapped add check
+$hline->add(6, 2, 6);
+$hline->add(6, 3, 4);
+is($hline->dump, <<EOS, "check internal overlap merged");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+EOS
+
+# white box test: try to force reallocation of an entry
+for my $i (0..20) {
+ $hline->add(7, $i*2, 1);
+}
+is($hline->dump, <<EOS, "lots of segments");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+ 7 (21): [0, 1) [2, 3) [4, 5) [6, 7) [8, 9) [10, 11) [12, 13) [14, 15) [16, 17) [18, 19) [20, 21) [22, 23) [24, 25) [26, 27) [28, 29) [30, 31) [32, 33) [34, 35) [36, 37) [38, 39) [40, 41)
+EOS
+# now merge them
+$hline->add(7, 1, 39);
+is($hline->dump, <<EOS, "merge lots of segments");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+ 7 (1): [0, 41)
+EOS
+
+# clean object
+$hline = Imager::Internal::Hlines::new(50, 50, 50, 50);
+$base_text = 'start_y: 50 limit_y: 100 start_x: 50 limit_x: 100';
+
+# left merge
+$hline->add(51, 45, 10);
+$hline->add(51, 55, 4);
+is($hline->dump, <<EOS, "left merge");
+$base_text
+ 51 (1): [50, 59)
+EOS
+
+# right merge
+$hline->add(52, 90, 5);
+$hline->add(52, 87, 5);
+is($hline->dump, <<EOS, "right merge");
+$base_text
+ 51 (1): [50, 59)
+ 52 (1): [87, 95)
+EOS
+
+undef $hline;
Imager::IO T_PTROBJ
Imager::Font::FT2 T_PTROBJ
Imager::FillHandle T_PTROBJ
+Imager::Internal::Hlines T_PTROBJ
const char * T_PV
float T_FLOAT
float* T_ARRAY