3 use Test::More tests => 142;
4 use Imager qw(:all :handy);
5 use Imager::Test qw(is_color3 is_fcolor3);
6 init_log("testout/t020masked.log", 1);
8 my $base_rgb = Imager::ImgRaw::new(100, 100, 3);
9 # put something in there
10 my $black = NC(0, 0, 0);
11 my $red = NC(255, 0, 0);
12 my $green = NC(0, 255, 0);
13 my $blue = NC(0, 0, 255);
14 my $white = NC(255, 255, 255);
15 my $grey = NC(128, 128, 128);
16 use Imager::Color::Float;
17 my $redf = Imager::Color::Float->new(1, 0, 0);
18 my $greenf = Imager::Color::Float->new(0, 1, 0);
19 my $bluef = Imager::Color::Float->new(0, 0, 1);
20 my $greyf = Imager::Color::Float->new(0.5, 0.5, 0.5);
21 my @cols = ($red, $green, $blue);
23 Imager::i_plin($base_rgb, 0, $y, ($cols[$y % 3] ) x 100);
26 # first a simple subset image
27 my $s_rgb = Imager::i_img_masked_new($base_rgb, undef, 25, 25, 50, 50);
29 is(Imager::i_img_getchannels($s_rgb), 3,
30 "1 channel image channel count match");
31 ok(Imager::i_img_getmask($s_rgb) & 1,
32 "1 channel image mask");
33 ok(Imager::i_img_virtual($s_rgb),
34 "1 channel image thinks it isn't virtual");
35 is(Imager::i_img_bits($s_rgb), 8,
36 "1 channel image has bits == 8");
37 is(Imager::i_img_type($s_rgb), 0, # direct
38 "1 channel image is direct");
40 my @ginfo = i_img_info($s_rgb);
41 is($ginfo[0], 50, "check width");
42 is($ginfo[1], 50, "check height");
44 # sample some pixels through the subset
45 my $c = Imager::i_get_pixel($s_rgb, 0, 0);
46 is_color3($c, 0, 255, 0, "check (0,0)");
47 $c = Imager::i_get_pixel($s_rgb, 49, 49);
49 is_color3($c, 0, 0, 255, "check (49,49)");
53 Imager::i_plin($s_rgb, 0, $y, ($cols[$y % 3]) x 50);
55 pass("managed to write to it");
56 # and checking the target image
57 $c = Imager::i_get_pixel($base_rgb, 25, 25);
58 is_color3($c, 255, 0, 0, "check (25,25)");
59 $c = Imager::i_get_pixel($base_rgb, 29, 29);
60 is_color3($c, 0, 255, 0, "check (29,29)");
66 Imager::i_plin($base_rgb, 0, $y, ($red ) x 100);
68 my $mask = Imager::ImgRaw::new(50, 50, 1);
69 # some venetian blinds
71 Imager::i_plin($mask, 5, $y*2, ($white) x 40);
73 # with a strip down the middle
75 Imager::i_plin($mask, 20, $y, ($white) x 8);
77 my $m_rgb = Imager::i_img_masked_new($base_rgb, $mask, 25, 25, 50, 50);
78 ok($m_rgb, "make masked with mask");
80 Imager::i_plin($m_rgb, 0, $y, ($green) x 50);
85 [ 25+19, 25+0, $red ],
86 [ 25+20, 25+0, $green ],
87 [ 25+27, 25+0, $green ],
88 [ 25+28, 25+0, $red ],
89 [ 25+49, 25+0, $red ],
90 [ 25+19, 25+7, $red ],
91 [ 25+19, 25+8, $green ],
92 [ 25+19, 25+9, $red ],
95 [ 25+5, 25+8, $green ],
96 [ 25+44, 25+8, $green ],
97 [ 25+45, 25+8, $red ],
98 [ 25+49, 25+49, $red ],
101 for my $test (@color_tests) {
102 my ($x, $y, $testc) = @$test;
103 my ($r, $g, $b) = $testc->rgba;
104 my $c = Imager::i_get_pixel($base_rgb, $x, $y);
105 is_color3($c, $r, $g, $b, "at ($x, $y)");
109 # tests for the OO versions, fairly simple, since the basic functionality
110 # is covered by the low-level interface tests
112 my $base = Imager->new(xsize=>100, ysize=>100);
113 ok($base, "make base OO image");
114 $base->box(color=>$blue, filled=>1); # fill it all
115 my $mask = Imager->new(xsize=>80, ysize=>80, channels=>1);
116 $mask->box(color=>$white, filled=>1, xmin=>5, xmax=>75, ymin=>5, ymax=>75);
117 my $m_img = $base->masked(mask=>$mask, left=>5, top=>5);
118 ok($m_img, "make masked OO image");
119 is($m_img->getwidth, 80, "check width");
120 $m_img->box(color=>$green, filled=>1);
121 my $c = $m_img->getpixel(x=>0, y=>0);
122 is_color3($c, 0, 0, 255, "check (0,0)");
123 $c = $m_img->getpixel(x => 5, y => 5);
124 is_color3($c, 0, 255, 0, "check (5,5)");
126 # older versions destroyed the Imager::ImgRaw object manually in
127 # Imager::DESTROY rather than letting Imager::ImgRaw::DESTROY
129 # so we test here by destroying the base and mask objects and trying
130 # to draw to the masked wrapper
131 # you may need to test with ElectricFence to trigger the problem
134 $m_img->box(color=>$blue, filled=>1);
135 pass("didn't crash unreffing base or mask for masked image");
138 # 35.7% cover on maskimg.c up to here
141 my $base = Imager->new(xsize => 100, ysize => 100);
142 ok($base, "make base");
143 { # make masked image subset outside of the base image
144 my $masked = $base->masked(left => 100);
145 ok (!$masked, "fail to make empty masked");
146 is($base->errstr, "subset outside of target image", "check message");
151 my $base = Imager->new(xsize => 10, ysize => 10);
152 ok($base, "make base for size limit tests");
154 my $masked = $base->masked(left => 5, right => 15);
155 ok($masked, "make masked");
156 is($masked->getwidth, 5, "check width truncated");
159 my $masked = $base->masked(top => 5, bottom => 15);
160 ok($masked, "make masked");
161 is($masked->getheight, 5, "check height truncated");
166 $mask = Imager->new(xsize => 80, ysize => 80, channels => 1);
167 $mask->box(filled => 1, color => $white, xmax => 39, ymax => 39);
168 $mask->box(fill => { hatch => "check1x1" }, ymin => 40, xmax => 39);
171 my $base = Imager->new(xsize => 100, ysize => 100, bits => "double");
172 ok($base, "base for single pixel tests");
173 is($base->type, "direct", "check type");
174 my $masked = $base->masked(mask => $mask, left => 1, top => 2);
175 my $limited = $base->masked(left => 1, top => 2);
177 is($masked->type, "direct", "check masked is same type as base");
178 is($limited->type, "direct", "check limited is same type as base");
181 # single pixel writes, masked
183 ok($masked->setpixel(x => 1, y => 3, color => $green),
184 "set (1,3) in masked (2, 5) in based");
185 my $c = $base->getpixel(x => 2, y => 5);
186 is_color3($c, 0, 255, 0, "check it wrote through");
187 ok($masked->setpixel(x => 45, y => 2, color => $red),
188 "set (45,2) in masked (46,4) in base (no mask)");
189 $c = $base->getpixel(x => 46, y => 4);
190 is_color3($c, 0, 0, 0, "shouldn't have written through");
193 ok($masked->setpixel(x => 2, y => 3, color => $redf),
194 "write float red to (2,3) base(3,5)");
195 my $c = $base->getpixel(x => 3, y => 5);
196 is_color3($c, 255, 0, 0, "check it wrote through");
197 ok($masked->setpixel(x => 45, y => 3, color => $greenf),
198 "set float (45,3) in masked (46,5) in base (no mask)");
199 $c = $base->getpixel(x => 46, y => 5);
200 is_color3($c, 0, 0, 0, "check it didn't write");
203 # write out of range should fail
204 ok(!$masked->setpixel(x => 80, y => 0, color => $green),
205 "write 8-bit color out of range");
206 ok(!$masked->setpixel(x => 0, y => 80, color => $greenf),
207 "write float color out of range");
214 note("plin coverage");
216 $base->box(filled => 1, color => $black);
219 is($masked->setscanline(x => 76, y => 1, pixels => [ ($red, $green) x 3 ]),
220 4, "try to write 6 pixels, but only write 4");
221 is_deeply([ $base->getsamples(x => 77, y => 3, width => 4) ],
223 "check not written through");
225 is($masked->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
226 72, "write many pixels (masked)");
227 is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
228 [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 9,
230 "check written through to base");
232 # simple path, due to number of transitions
233 is($masked->setscanline(x => 0, y => 40, pixels => [ ($red, $green, $blue, $grey) x 5 ]),
234 20, "try to write 20 pixels, with alternating write through");
235 is_deeply([ $base->getsamples(x => 1, y => 42, width => 20) ],
236 [ ( (0, 0, 0), (0,255,0), (0,0,0), (128,128,128) ) x 5 ],
237 "check correct pixels written through");
241 $base->box(filled => 1, color => $black);
242 # plin, non-masked path
243 is($limited->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
244 72, "write many pixels (limited)");
245 is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
246 [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 18 ],
247 "check written through to based");
252 is($masked->setscanline(x => 80, y => 2, pixels => [ $red, $green ]),
253 0, "check writing no pixels");
258 note("plinf coverage");
260 $base->box(filled => 1, color => $black);
263 is($masked->setscanline(x => 76, y => 1, pixels => [ ($redf, $greenf) x 3 ]),
264 4, "try to write 6 pixels, but only write 4");
265 is_deeply([ $base->getsamples(x => 77, y => 3, width => 4, type => "float") ],
267 "check not written through");
269 is($masked->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
270 72, "write many pixels (masked)");
271 is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
272 [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 9,
274 "check written through to base");
276 # simple path, due to number of transitions
277 is($masked->setscanline(x => 0, y => 40, pixels => [ ($redf, $greenf, $bluef, $greyf) x 5 ]),
278 20, "try to write 20 pixels, with alternating write through");
279 is_deeply([ $base->getsamples(x => 1, y => 42, width => 20, type => "float") ],
280 [ ( (0, 0, 0), (0,1,0), (0,0,0), (0.5,0.5,0.5) ) x 5 ],
281 "check correct pixels written through");
285 $base->box(filled => 1, color => $black);
286 # plinf, non-masked path
287 is($limited->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
288 72, "write many pixels (limited)");
289 is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
290 [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 18 ],
291 "check written through to based");
296 is($masked->setscanline(x => 80, y => 2, pixels => [ $redf, $greenf ]),
297 0, "check writing no pixels");
305 $base->box(filled => 1, color => $black);
306 ok($base->setpixel(x => 4, y => 10, color => $red),
307 "set base(4,10) to red");
308 is_fcolor3($masked->getpixel(x => 3, y => 8),
309 255, 0, 0, "check pixel written");
312 is($masked->getpixel(x => -1, y => 1),
313 undef, "check failure to left");
314 is($masked->getpixel(x => 0, y => -1),
315 undef, "check failure to top");
316 is($masked->getpixel(x => 80, y => 1),
317 undef, "check failure to right");
318 is($masked->getpixel(x => 0, y => 80),
319 undef, "check failure to bottom");
324 $base->box(filled => 1, color => $black);
325 ok($base->setpixel(x => 4, y => 10, color => $redf),
326 "set base(4,10) to red");
327 is_fcolor3($masked->getpixel(x => 3, y => 8, type => "float"),
328 1.0, 0, 0, 0, "check pixel written");
331 is($masked->getpixel(x => -1, y => 1, type => "float"),
332 undef, "check failure to left");
333 is($masked->getpixel(x => 0, y => -1, type => "float"),
334 undef, "check failure to top");
335 is($masked->getpixel(x => 80, y => 1, type => "float"),
336 undef, "check failure to right");
337 is($masked->getpixel(x => 0, y => 80, type => "float"),
338 undef, "check failure to bottom");
345 $base->box(filled => 1, color => $black);
346 is($base->setscanline(x => 31, y => 3,
347 pixels => [ ( $red, $green) x 10 ]),
348 20, "write 20 pixels to base image");
349 my @colors = $masked->
350 getscanline(x => 30, y => 1, width => 20);
351 is(@colors, 20, "check we got right number of colors");
352 is_color3($colors[0], 255, 0, 0, "check first pixel");
353 is_color3($colors[19], 0, 255, 0, "check last pixel");
355 @colors = $masked->getscanline(x => 76, y => 2, width => 10);
356 is(@colors, 4, "read line from right edge");
357 is_color3($colors[0], 0, 0, 0, "check pixel");
359 is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1) ],
360 [], "fail read left of image");
361 is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1) ],
362 [], "fail read top of image");
363 is_deeply([$masked->getscanline(x => 80, y => 0, width => 1)],
364 [], "fail read right of image");
365 is_deeply([$masked->getscanline(x => 0, y => 80, width => 1)],
366 [], "fail read bottom of image");
370 $base->box(filled => 1, color => $black);
371 is($base->setscanline(x => 31, y => 3,
372 pixels => [ ( $redf, $greenf) x 10 ]),
373 20, "write 20 pixels to base image");
374 my @colors = $masked->
375 getscanline(x => 30, y => 1, width => 20, type => "float");
376 is(@colors, 20, "check we got right number of colors");
377 is_fcolor3($colors[0], 1.0, 0, 0, 0, "check first pixel");
378 is_fcolor3($colors[19], 0, 1.0, 0, 0, "check last pixel");
381 getscanline(x => 76, y => 2, width => 10, type => "float");
382 is(@colors, 4, "read line from right edge");
383 is_fcolor3($colors[0], 0, 0, 0, 0, "check pixel");
385 is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1, type => "float") ],
386 [], "fail read left of image");
387 is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1, type => "float") ],
388 [], "fail read top of image");
389 is_deeply([$masked->getscanline(x => 80, y => 0, width => 1, type => "float")],
390 [], "fail read right of image");
391 is_deeply([$masked->getscanline(x => 0, y => 80, width => 1, type => "float")],
392 [], "fail read bottom of image");
399 $base->box(filled => 1, color => $black);
400 is($base->setscanline(x => 31, y => 3,
401 pixels => [ ( $red, $green) x 10 ]),
402 20, "write 20 pixels to base image");
403 my @samps = $masked->
404 getsamples(x => 30, y => 1, width => 20);
405 is(@samps, 60, "check we got right number of samples");
407 [ (255, 0, 0, 0, 255, 0) x 10 ],
411 getsamples(x => 76, y => 2, width => 10);
412 is(@samps, 12, "read line from right edge");
413 is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
415 is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1) ],
416 [], "fail read left of image");
417 is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1) ],
418 [], "fail read top of image");
419 is_deeply([$masked->getsamples(x => 80, y => 0, width => 1)],
420 [], "fail read right of image");
421 is_deeply([$masked->getsamples(x => 0, y => 80, width => 1)],
422 [], "fail read bottom of image");
426 $base->box(filled => 1, color => $black);
427 is($base->setscanline(x => 31, y => 3,
428 pixels => [ ( $redf, $greenf) x 10 ]),
429 20, "write 20 pixels to base image");
430 my @samps = $masked->
431 getsamples(x => 30, y => 1, width => 20, type => "float");
432 is(@samps, 60, "check we got right number of samples");
434 [ (1.0, 0, 0, 0, 1.0, 0) x 10 ],
438 getsamples(x => 76, y => 2, width => 10, type => "float");
439 is(@samps, 12, "read line from right edge");
440 is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
442 is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1, type => "float") ],
443 [], "fail read left of image");
444 is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1, type => "float") ],
445 [], "fail read top of image");
446 is_deeply([$masked->getsamples(x => 80, y => 0, width => 1, type => "float")],
447 [], "fail read right of image");
448 is_deeply([$masked->getsamples(x => 0, y => 80, width => 1, type => "float")],
449 [], "fail read bottom of image");
456 my $base = Imager->new(xsize => 100, ysize => 100, type => "paletted");
457 ok($base, "make paletted base");
458 is($base->type, "paletted", "check we got paletted");
459 is($base->addcolors(colors => [ $black, $red, $green, $blue ]),
461 "add some colors to base");
462 my $masked = $base->masked(mask => $mask, left => 1, top => 2);
463 my $limited = $base->masked(left => 1, top => 2);
465 is($masked->type, "paletted", "check masked is same type as base");
466 is($limited->type, "paletted", "check limited is same type as base");
469 # make sure addcolors forwarded
470 is($masked->addcolors(colors => [ $grey ]), 4,
471 "test addcolors forwarded");
472 my @colors = $masked->getcolors();
473 is(@colors, 5, "check getcolors forwarded");
474 is_color3($colors[1], 255, 0, 0, "check color from palette");
477 my ($blacki, $redi, $greeni, $bluei, $greyi) = 0 .. 4;
481 $base->box(filled => 1, color => $black);
482 is($base->setscanline(x => 0, y => 5, type => "index",
483 pixels => [ ( $redi, $greeni, $bluei, $greyi) x 25 ]),
484 100, "write some pixels to base");
485 my @indexes = $masked->getscanline(y => 3, type => "index", width => "81");
486 is(@indexes, 80, "got 80 indexes");
488 [ ( $greeni, $bluei, $greyi, $redi) x 20 ],
491 is_deeply([ $masked->getscanline(x => -1, y => 3, type => "index") ],
492 [], "fail read left of image");
498 $base->box(filled => 1, color => $black);
499 is($limited->setscanline(x => 1, y => 1, type => "index",
500 pixels => [ ( $redi, $greeni, $bluei) x 3 ]),
502 is_deeply([ $base->getscanline(x => 2, y => 3, type => "index",
504 [ ( $redi, $greeni, $bluei) x 3 ],
505 "check set in base");
508 $base->box(filled => 1, color => $black);
509 is($masked->setscanline(x => 1, y => 2, type => "index",
510 pixels => [ ( $redi, $greeni, $bluei, $greyi) x 12 ]),
512 is_deeply([ $base->getscanline(x => 0, y => 4, type => "index") ],
514 ( $redi, $greeni, $bluei, $greyi ) x 9,
515 $redi, $greeni, $bluei, ( 0 ) x 59 ],
520 is($masked->setscanline(x => -1, y => 0, type => "index",
521 pixels => [ $redi, $bluei ]),
522 0, "fail to write ppal");
524 is($masked->setscanline(x => 78, y => 0, type => "index",
525 pixels => [ $redi, $bluei, $greeni, $greyi ]),
526 2, "write over right side");