]> git.imager.perl.org - imager.git/blob - t/t020masked.t
fix formatting and spelling to match the rest
[imager.git] / t / t020masked.t
1 #!perl -w
2 use strict;
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);
7
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);
22 for my $y (0..99) {
23   Imager::i_plin($base_rgb, 0, $y, ($cols[$y % 3] ) x 100);
24 }
25
26 # first a simple subset image
27 my $s_rgb = Imager::i_img_masked_new($base_rgb, undef, 25, 25, 50, 50);
28
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");
39
40 my @ginfo = i_img_info($s_rgb);
41 is($ginfo[0], 50, "check width");
42 is($ginfo[1], 50, "check height");
43
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);
48 # (25+49)%3 = 2
49 is_color3($c, 0, 0, 255, "check (49,49)");
50
51 # try writing to it
52 for my $y (0..49) {
53   Imager::i_plin($s_rgb, 0, $y, ($cols[$y % 3]) x 50);
54 }
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)");
61
62 undef $s_rgb;
63
64 # a basic background
65 for my $y (0..99) {
66   Imager::i_plin($base_rgb, 0, $y, ($red ) x 100);
67 }
68 my $mask = Imager::ImgRaw::new(50, 50, 1);
69 # some venetian blinds
70 for my $y (4..20) {
71   Imager::i_plin($mask, 5, $y*2, ($white) x 40);
72 }
73 # with a strip down the middle
74 for my $y (0..49) {
75   Imager::i_plin($mask, 20, $y, ($white) x 8);
76 }
77 my $m_rgb = Imager::i_img_masked_new($base_rgb, $mask, 25, 25, 50, 50);
78 ok($m_rgb, "make masked with mask");
79 for my $y (0..49) {
80   Imager::i_plin($m_rgb, 0, $y, ($green) x 50);
81 }
82 my @color_tests =
83   (
84    [ 25+0,  25+0,  $red ],
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 ],
93    [ 25+0,  25+8,  $red ],
94    [ 25+4,  25+8,  $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 ],
99   );
100 my $test_num = 15;
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)");
106 }
107
108 {
109   # tests for the OO versions, fairly simple, since the basic functionality
110   # is covered by the low-level interface tests
111    
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)");
125
126   # older versions destroyed the Imager::ImgRaw object manually in 
127   # Imager::DESTROY rather than letting Imager::ImgRaw::DESTROY 
128   # destroy the object
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
132   undef $mask;
133   undef $base;
134   $m_img->box(color=>$blue, filled=>1);
135   pass("didn't crash unreffing base or mask for masked image");
136 }
137
138 # 35.7% cover on maskimg.c up to here
139
140 { # error handling:
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");
147   }
148 }
149
150 { # size limiting
151   my $base = Imager->new(xsize => 10, ysize => 10);
152   ok($base, "make base for size limit tests");
153   {
154     my $masked = $base->masked(left => 5, right => 15);
155     ok($masked, "make masked");
156     is($masked->getwidth, 5, "check width truncated");
157   }
158   {
159     my $masked = $base->masked(top => 5, bottom => 15);
160     ok($masked, "make masked");
161     is($masked->getheight, 5, "check height truncated");
162   }
163 }
164 # 36.7% up to here
165
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);
169
170 {
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);
176
177   is($masked->type, "direct", "check masked is same type as base");
178   is($limited->type, "direct", "check limited is same type as base");
179
180   {
181     # single pixel writes, masked
182     {
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");
191     }
192     {
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");
201     }
202     {
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");
208     }
209   }
210
211   # 46.9
212
213   {
214     note("plin coverage");
215     {
216       $base->box(filled => 1, color => $black);
217       # plin masked
218       # simple path
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) ],
222                 [ ( 0 ) x 12 ],
223                 "check not written through");
224       # !simple path
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,
229                   ( 0, 0, 0 ) x 36 ],
230                 "check written through to base");
231       
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");
238     }
239     
240     {
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");
248     }
249     
250     {
251       # draw outside fails
252       is($masked->setscanline(x => 80, y => 2, pixels => [ $red, $green ]),
253          0, "check writing no pixels");
254     }
255   }
256
257   {
258     note("plinf coverage");
259     {
260       $base->box(filled => 1, color => $black);
261       # plinf masked
262       # simple path
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") ],
266                 [ ( 0 ) x 12 ],
267                 "check not written through");
268       # !simple path
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,
273                   ( 0, 0, 0 ) x 36 ],
274                 "check written through to base");
275       
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");
282     }
283     
284     {
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");
292     }
293     
294     {
295       # draw outside fails
296       is($masked->setscanline(x => 80, y => 2, pixels => [ $redf, $greenf ]),
297          0, "check writing no pixels");
298     }
299   }
300   # 71.4%
301   {
302     {
303       note("gpix");
304       # gpix
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");
310
311       # out of range
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");
320     }
321     {
322       note("gpixf");
323       # gpixf
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");
329
330       # out of range
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");
339     }
340   }
341   # 74.5
342   {
343     {
344       note("glin");
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");
354
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");
358
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");
367     }
368     {
369       note("glinf");
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");
379
380       @colors = $masked->
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");
384
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");
393     }
394   }
395   # 81.6%
396   {
397     {
398       note("gsamp");
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");
406       is_deeply(\@samps,
407                 [ (255, 0, 0, 0, 255, 0) x 10 ],
408                 "check it");
409
410       @samps = $masked->
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");
414
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");
423     }
424     {
425       note("gsampf");
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");
433       is_deeply(\@samps,
434                 [ (1.0, 0, 0, 0, 1.0, 0) x 10 ],
435                 "check it");
436
437       @samps = $masked->
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");
441
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");
450     }
451   }
452   # 86.2%
453 }
454
455 {
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 ]),
460      "0 but true",
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);
464
465   is($masked->type, "paletted", "check masked is same type as base");
466   is($limited->type, "paletted", "check limited is same type as base");
467
468   {
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");
475   }
476
477   my ($blacki, $redi, $greeni, $bluei, $greyi) = 0 .. 4;
478
479   { # gpal
480     note("gpal");
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");
487     is_deeply(\@indexes,
488               [ ( $greeni, $bluei, $greyi, $redi) x 20 ],
489               "check values");
490
491     is_deeply([ $masked->getscanline(x => -1, y => 3, type => "index") ],
492               [], "fail read left of image");
493   }
494   # 89.8%
495
496   { # ppal, unmasked
497     note("ppal");
498     $base->box(filled => 1, color => $black);
499     is($limited->setscanline(x => 1, y => 1, type => "index",
500                              pixels => [ ( $redi, $greeni, $bluei) x 3 ]),
501        9, "ppal limited");
502     is_deeply([ $base->getscanline(x => 2, y => 3, type => "index", 
503                                    width => 9) ],
504               [ ( $redi, $greeni, $bluei) x 3 ],
505               "check set in base");
506   }
507   { # ppal, masked
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 ]),
511        48, "ppal masked");
512     is_deeply([ $base->getscanline(x => 0, y => 4, type => "index") ],
513               [ 0, 0,
514                 ( $redi, $greeni, $bluei, $greyi ) x 9,
515                 $redi, $greeni, $bluei, ( 0 ) x 59 ],
516               "check written");
517   }
518   {
519     # ppal, errors
520     is($masked->setscanline(x => -1, y => 0, type => "index",
521                             pixels => [ $redi, $bluei ]),
522        0, "fail to write ppal");
523
524     is($masked->setscanline(x => 78, y => 0, type => "index",
525                            pixels => [ $redi, $bluei, $greeni, $greyi ]),
526        2, "write over right side");
527   }
528 }