commit changes from draw branch
[imager.git] / t / t20fill.t
CommitLineData
f1ac5027
TC
1#!perl -w
2use strict;
9b1ec2b8 3use Test::More tests => 121;
efdc2568 4
f1ac5027
TC
5use Imager ':handy';
6use Imager::Fill;
7use Imager::Color::Float;
f3b59de8 8use Config;
f1ac5027 9
efdc2568 10Imager::init_log("testout/t20fill.log", 1);
f1ac5027
TC
11
12my $blue = NC(0,0,255);
13my $red = NC(255, 0, 0);
14my $redf = Imager::Color::Float->new(1, 0, 0);
e2d5ca90 15my $bluef = Imager::Color::Float->new(0, 0, 1);
f1ac5027 16my $rsolid = Imager::i_new_fill_solid($blue, 0);
109bec2d 17ok($rsolid, "building solid fill");
f1ac5027
TC
18my $raw1 = Imager::ImgRaw::new(100, 100, 3);
19# use the normal filled box
20Imager::i_box_filled($raw1, 0, 0, 99, 99, $blue);
21my $raw2 = Imager::ImgRaw::new(100, 100, 3);
22Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid);
109bec2d 23ok(1, "drawing with solid fill");
f1ac5027 24my $diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 25ok($diff == 0, "solid fill doesn't match");
f1ac5027
TC
26Imager::i_box_filled($raw1, 0, 0, 99, 99, $red);
27my $rsolid2 = Imager::i_new_fill_solidf($redf, 0);
109bec2d 28ok($rsolid2, "creating float solid fill");
f1ac5027
TC
29Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid2);
30$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 31ok($diff == 0, "float solid fill doesn't match");
f1ac5027
TC
32
33# ok solid still works, let's try a hatch
34# hash1 is a 2x2 checkerboard
35my $rhatcha = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 0, 0);
36my $rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 2, 0);
109bec2d 37ok($rhatcha && $rhatchb, "can't build hatched fill");
f1ac5027
TC
38
39# the offset should make these match
40Imager::i_box_cfill($raw1, 0, 0, 99, 99, $rhatcha);
41Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
109bec2d 42ok(1, "filling with hatch");
f1ac5027 43$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 44ok($diff == 0, "hatch images different");
f1ac5027
TC
45$rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 4, 6);
46Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
47$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 48ok($diff == 0, "hatch images different");
f1ac5027
TC
49
50# I guess I was tired when I originally did this - make sure it keeps
51# acting the way it's meant to
52# I had originally expected these to match with the red and blue swapped
53$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 2, 2);
54Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
55$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 56ok($diff == 0, "hatch images different");
f1ac5027
TC
57
58# this shouldn't match
59$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 1, 1);
60Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
61$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 62ok($diff, "hatch images the same!");
f1ac5027
TC
63
64# custom hatch
65# the inverse of the 2x2 checkerboard
66my $hatch = pack("C8", 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC);
67my $rcustom = Imager::i_new_fill_hatch($blue, $red, 0, 0, $hatch, 0, 0);
68Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rcustom);
69$diff = Imager::i_img_diff($raw1, $raw2);
109bec2d 70ok(!$diff, "custom hatch mismatch");
f1ac5027 71
e2d5ca90
TC
72{
73 # basic test of floating color hatch fills
74 # this will exercise the code that the gcc shipped with OS X 10.4
75 # forgets to generate
76 # the float version is called iff we're working with a non-8-bit image
77 # i_new_fill_hatchf() makes the same object as i_new_fill_hatch() but
78 # we test the other construction code path here
79 my $fraw1 = Imager::i_img_double_new(100, 100, 3);
80 my $fhatch1 = Imager::i_new_fill_hatchf($redf, $bluef, 0, 1, undef, 0, 0);
81 ok($fraw1, "making double image 1");
82 ok($fhatch1, "making float hatch 1");
83 Imager::i_box_cfill($fraw1, 0, 0, 99, 99, $fhatch1);
84 my $fraw2 = Imager::i_img_double_new(100, 100, 3);
85 my $fhatch2 = Imager::i_new_fill_hatchf($bluef, $redf, 0, 1, undef, 0, 2);
86 ok($fraw2, "making double image 2");
87 ok($fhatch2, "making float hatch 2");
88 Imager::i_box_cfill($fraw2, 0, 0, 99, 99, $fhatch2);
89
90 $diff = Imager::i_img_diff($fraw1, $fraw2);
91 ok(!$diff, "float custom hatch mismatch");
92 save($fraw1, "testout/t20hatchf1.ppm");
93 save($fraw2, "testout/t20hatchf2.ppm");
94}
95
f1ac5027
TC
96# test the oo interface
97my $im1 = Imager->new(xsize=>100, ysize=>100);
98my $im2 = Imager->new(xsize=>100, ysize=>100);
99
efdc2568 100my $solid = Imager::Fill->new(solid=>'#FF0000');
109bec2d
TC
101ok($solid, "creating oo solid fill");
102ok($solid->{fill}, "bad oo solid fill");
f1ac5027
TC
103$im1->box(fill=>$solid);
104$im2->box(filled=>1, color=>$red);
105$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
109bec2d 106ok(!$diff, "oo solid fill");
f1ac5027
TC
107
108my $hatcha = Imager::Fill->new(hatch=>'check2x2');
109my $hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2);
110$im1->box(fill=>$hatcha);
111$im2->box(fill=>$hatchb);
112# should be different
113$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
109bec2d 114ok($diff, "offset checks the same!");
f1ac5027
TC
115$hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2, dy=>2);
116$im2->box(fill=>$hatchb);
117$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
109bec2d 118ok(!$diff, "offset into similar check should be the same");
f1ac5027
TC
119
120# test dymanic build of fill
121$im2->box(fill=>{hatch=>'check2x2', dx=>2, fg=>NC(255,255,255),
122 bg=>NC(0,0,0)});
123$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
109bec2d 124ok(!$diff, "offset and flipped should be the same");
f1ac5027
TC
125
126# a simple demo
127my $im = Imager->new(xsize=>200, ysize=>200);
128
129$im->box(xmin=>10, ymin=>10, xmax=>190, ymax=>190,
130 fill=>{ hatch=>'check4x4',
131 fg=>NC(128, 0, 0),
569795e8
TC
132 bg=>NC(128, 64, 0) })
133 or print "# ",$im->errstr,"\n";
f1ac5027
TC
134$im->arc(r=>80, d1=>45, d2=>75,
135 fill=>{ hatch=>'stipple2',
136 combine=>1,
efdc2568 137 fg=>[ 0, 0, 0, 255 ],
569795e8
TC
138 bg=>{ rgba=>[255,255,255,160] } })
139 or print "# ",$im->errstr,"\n";
f1ac5027 140$im->arc(r=>80, d1=>75, d2=>135,
569795e8
TC
141 fill=>{ fountain=>'radial', xa=>100, ya=>100, xb=>20, yb=>100 })
142 or print "# ",$im->errstr,"\n";
f1ac5027
TC
143$im->write(file=>'testout/t20_sample.ppm');
144
cc6483e0
TC
145# flood fill tests
146my $rffimg = Imager::ImgRaw::new(100, 100, 3);
147# build a H
148Imager::i_box_filled($rffimg, 10, 10, 20, 90, $blue);
149Imager::i_box_filled($rffimg, 80, 10, 90, 90, $blue);
150Imager::i_box_filled($rffimg, 20, 45, 80, 55, $blue);
151my $black = Imager::Color->new(0, 0, 0);
152Imager::i_flood_fill($rffimg, 15, 15, $red);
153my $rffcmp = Imager::ImgRaw::new(100, 100, 3);
154# build a H
155Imager::i_box_filled($rffcmp, 10, 10, 20, 90, $red);
156Imager::i_box_filled($rffcmp, 80, 10, 90, 90, $red);
157Imager::i_box_filled($rffcmp, 20, 45, 80, 55, $red);
158$diff = Imager::i_img_diff($rffimg, $rffcmp);
109bec2d 159ok(!$diff, "flood fill difference");
cc6483e0
TC
160
161my $ffim = Imager->new(xsize=>100, ysize=>100);
162my $yellow = Imager::Color->new(255, 255, 0);
163$ffim->box(xmin=>10, ymin=>10, xmax=>20, ymax=>90, color=>$blue, filled=>1);
164$ffim->box(xmin=>20, ymin=>45, xmax=>80, ymax=>55, color=>$blue, filled=>1);
165$ffim->box(xmin=>80, ymin=>10, xmax=>90, ymax=>90, color=>$blue, filled=>1);
109bec2d 166ok($ffim->flood_fill('x'=>50, 'y'=>50, color=>$red), "flood fill");
cc6483e0 167$diff = Imager::i_img_diff($rffcmp, $ffim->{IMG});
109bec2d 168ok(!$diff, "oo flood fill difference");
9d540150 169$ffim->flood_fill('x'=>50, 'y'=>50,
cc6483e0 170 fill=> {
3efb0915
TC
171 hatch => 'check2x2',
172 fg => '0000FF',
cc6483e0
TC
173 });
174# fill=>{
175# fountain=>'radial',
176# xa=>50, ya=>50,
177# xb=>10, yb=>10,
178# });
179$ffim->write(file=>'testout/t20_ooflood.ppm');
180
3efb0915
TC
181my $copy = $ffim->copy;
182ok($ffim->flood_fill('x' => 50, 'y' => 50,
183 color => $red, border => '000000'),
184 "border solid flood fill");
185is(Imager::i_img_diff($ffim->{IMG}, $rffcmp), 0, "compare");
186ok($ffim->flood_fill('x' => 50, 'y' => 50,
187 fill => { hatch => 'check2x2', fg => '0000FF', },
188 border => '000000'),
189 "border cfill fill");
190is(Imager::i_img_diff($ffim->{IMG}, $copy->{IMG}), 0,
191 "compare");
192
efdc2568
TC
193# test combining modes
194my $fill = NC(192, 128, 128, 128);
195my $target = NC(64, 32, 64);
9b1ec2b8 196my $trans_target = NC(64, 32, 64, 128);
efdc2568
TC
197my %comb_tests =
198 (
9b1ec2b8
TC
199 none=>
200 {
201 opaque => $fill,
202 trans => $fill,
203 },
204 normal=>
205 {
206 opaque => NC(128, 80, 96),
207 trans => NC(150, 96, 107, 191),
208 },
209 multiply =>
210 {
211 opaque => NC(56, 24, 48),
212 trans => NC(101, 58, 74, 192),
213 },
214 dissolve =>
215 {
216 opaque => [ $target, NC(192, 128, 128, 255) ],
217 trans => [ $trans_target, NC(192, 128, 128, 255) ],
218 },
219 add =>
220 {
221 opaque => NC(159, 96, 128),
222 trans => NC(128, 80, 96, 255),
223 },
224 subtract =>
225 {
226 opaque => NC(0, 0, 0),
227 trans => NC(0, 0, 0, 255),
228 },
229 diff =>
230 {
231 opaque => NC(96, 64, 64),
232 trans => NC(127, 85, 85, 192),
233 },
234 lighten =>
235 {
236 opaque => NC(128, 80, 96),
237 trans => NC(149, 95, 106, 192),
238 },
239 darken =>
240 {
241 opaque => $target,
242 trans => NC(106, 63, 85, 192),
243 },
efdc2568
TC
244 # the following results are based on the results of the tests and
245 # are suspect for that reason (and were broken at one point <sigh>)
246 # but trying to work them out manually just makes my head hurt - TC
9b1ec2b8
TC
247 hue =>
248 {
249 opaque => NC(64, 32, 47),
250 trans => NC(64, 32, 42, 128),
251 },
252 saturation =>
253 {
254 opaque => NC(63, 37, 64),
255 trans => NC(64, 39, 64, 128),
256 },
257 value =>
258 {
259 opaque => NC(127, 64, 128),
260 trans => NC(149, 75, 150, 128),
261 },
262 color =>
263 {
264 opaque => NC(64, 37, 52),
265 trans => NC(64, 39, 50, 128),
266 },
efdc2568
TC
267 );
268
efdc2568
TC
269for my $comb (Imager::Fill->combines) {
270 my $test = $comb_tests{$comb};
efdc2568 271 my $fillobj = Imager::Fill->new(solid=>$fill, combine=>$comb);
9b1ec2b8
TC
272
273 for my $bits (qw(8 double)) {
274 {
275 my $targim = Imager->new(xsize=>4, ysize=>4, bits => $bits);
276 $targim->box(filled=>1, color=>$target);
277 $targim->box(fill=>$fillobj);
278 my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
279 my $allowed = $test->{opaque};
280 $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
281 ok(scalar grep(color_close($_, $c), @$allowed),
282 "opaque '$comb' $bits bits")
283 or print "# got:",join(",", $c->rgba)," allowed: ",
284 join("|", map { join(",", $_->rgba) } @$allowed),"\n";
285 }
286
287 {
288 # make sure the alpha path in the combine function produces the same
289 # or at least as sane a result as the non-alpha path
290 my $targim = Imager->new(xsize=>4, ysize=>4, channels => 4, bits => $bits);
291 $targim->box(filled=>1, color=>$target);
292 $targim->box(fill=>$fillobj);
293 my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
294 my $allowed = $test->{opaque};
295 $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
296 ok(scalar grep(color_close4($_, $c), @$allowed),
297 "opaque '$comb' 4-channel $bits bits")
298 or print "# got:",join(",", $c->rgba)," allowed: ",
299 join("|", map { join(",", $_->rgba) } @$allowed),"\n";
300 }
301
302 {
303 my $transim = Imager->new(xsize => 4, ysize => 4, channels => 4, bits => $bits);
304 $transim->box(filled=>1, color=>$trans_target);
305 $transim->box(fill => $fillobj);
306 my $c = $transim->getpixel(x => 1, 'y' => 1);
307 my $allowed = $test->{trans};
308 $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
309 ok(scalar grep(color_close4($_, $c), @$allowed),
310 "translucent '$comb' $bits bits")
311 or print "# got:",join(",", $c->rgba)," allowed: ",
312 join("|", map { join(",", $_->rgba) } @$allowed),"\n";
313 }
efdc2568
TC
314 }
315}
316
109bec2d 317ok($ffim->arc(r=>45, color=>$blue, aa=>1), "aa circle");
0d321238
TC
318$ffim->write(file=>"testout/t20_aacircle.ppm");
319
f576ce7e
TC
320# image based fills
321my $green = NC(0, 255, 0);
322my $fillim = Imager->new(xsize=>40, ysize=>40, channels=>4);
323$fillim->box(filled=>1, xmin=>5, ymin=>5, xmax=>35, ymax=>35,
324 color=>NC(0, 0, 255, 128));
325$fillim->arc(filled=>1, r=>10, color=>$green, aa=>1);
326my $ooim = Imager->new(xsize=>150, ysize=>150);
327$ooim->box(filled=>1, color=>$green, xmin=>70, ymin=>25, xmax=>130, ymax=>125);
328$ooim->box(filled=>1, color=>$blue, xmin=>20, ymin=>25, xmax=>80, ymax=>125);
329$ooim->arc(r=>30, color=>$red, aa=>1);
330
331my $oocopy = $ooim->copy();
109bec2d 332ok($oocopy->arc(fill=>{image=>$fillim,
f576ce7e
TC
333 combine=>'normal',
334 xoff=>5}, r=>40),
335 "image based fill");
336$oocopy->write(file=>'testout/t20_image.ppm');
337
338# a more complex version
339use Imager::Matrix2d ':handy';
340$oocopy = $ooim->copy;
109bec2d 341ok($oocopy->arc(fill=>{
f576ce7e
TC
342 image=>$fillim,
343 combine=>'normal',
344 matrix=>m2d_rotate(degrees=>30),
345 xoff=>5
346 }, r=>40),
347 "transformed image based fill");
348$oocopy->write(file=>'testout/t20_image_xform.ppm');
349
109bec2d 350ok(!$oocopy->arc(fill=>{ hatch=>"not really a hatch" }, r=>20),
569795e8 351 "error handling of automatic fill conversion");
109bec2d 352ok($oocopy->errstr =~ /Unknown hatch type/,
569795e8
TC
353 "error message for automatic fill conversion");
354
2de568dc
TC
355# previous box fills to float images, or using the fountain fill
356# got into a loop here
f3b59de8 357
109bec2d
TC
358SKIP:
359{
360 skip("can't test without alarm()", 1) unless $Config{d_alarm};
2de568dc
TC
361 local $SIG{ALRM} = sub { die; };
362
363 eval {
364 alarm(2);
109bec2d 365 ok($ooim->box(xmin=>20, ymin=>20, xmax=>80, ymax=>40,
2de568dc
TC
366 fill=>{ fountain=>'linear', xa=>20, ya=>20, xb=>80,
367 yb=>20 }), "linear box fill");
13b8afa0 368 alarm 0;
2de568dc 369 };
109bec2d 370 $@ and ok(0, "linear box fill $@");
f3b59de8 371}
2de568dc 372
109bec2d
TC
373# test that passing in a non-array ref returns an error
374{
375 my $fill = Imager::Fill->new(fountain=>'linear',
376 xa => 20, ya=>20, xb=>20, yb=>40,
377 segments=>"invalid");
378 ok(!$fill, "passing invalid segments produces an error");
379 cmp_ok(Imager->errstr, '=~', 'array reference',
380 "check the error message");
381}
f1ac5027 382
109bec2d
TC
383# test that colors in segments are converted
384{
385 my @segs =
386 (
387 [ 0.0, 0.5, 1.0, '000000', '#FFF', 0, 0 ],
388 );
389 my $fill = Imager::Fill->new(fountain=>'linear',
390 xa => 0, ya=>20, xb=>49, yb=>20,
391 segments=>\@segs);
392 ok($fill, "check that color names are converted")
393 or print "# ",Imager->errstr,"\n";
394 my $im = Imager->new(xsize=>50, ysize=>50);
395 $im->box(fill=>$fill);
396 my $left = $im->getpixel('x'=>0, 'y'=>20);
397 ok(color_close($left, Imager::Color->new(0,0,0)),
398 "check black converted correctly");
399 my $right = $im->getpixel('x'=>49, 'y'=>20);
400 ok(color_close($right, Imager::Color->new(255,255,255)),
401 "check white converted correctly");
402
403 # check that invalid colors handled correctly
404
405 my @segs2 =
406 (
407 [ 0.0, 0.5, 1.0, '000000', 'FxFxFx', 0, 0 ],
408 );
409 my $fill2 = Imager::Fill->new(fountain=>'linear',
410 xa => 0, ya=>20, xb=>49, yb=>20,
411 segments=>\@segs2);
412 ok(!$fill2, "check handling of invalid color names");
413 cmp_ok(Imager->errstr, '=~', 'No color named', "check error message");
efdc2568
TC
414}
415
416sub color_close {
417 my ($c1, $c2) = @_;
418
419 my @c1 = $c1->rgba;
420 my @c2 = $c2->rgba;
421
422 for my $i (0..2) {
423 if (abs($c1[$i]-$c2[$i]) > 2) {
424 return 0;
425 }
426 }
427 return 1;
f1ac5027
TC
428}
429
9b1ec2b8
TC
430sub color_close4 {
431 my ($c1, $c2) = @_;
432
433 my @c1 = $c1->rgba;
434 my @c2 = $c2->rgba;
435
436 for my $i (0..3) {
437 if (abs($c1[$i]-$c2[$i]) > 2) {
438 return 0;
439 }
440 }
441 return 1;
442}
443
f1ac5027
TC
444# for use during testing
445sub save {
446 my ($im, $name) = @_;
447
448 open FH, "> $name" or die "Cannot create $name: $!";
449 binmode FH;
450 my $io = Imager::io_new_fd(fileno(FH));
451 Imager::i_writeppm_wiol($im, $io) or die "Cannot save to $name";
452 undef $io;
453 close FH;
454}