Commit | Line | Data |
---|---|---|
1c5252ed | 1 | #!perl -w |
02d1d628 | 2 | |
1c5252ed | 3 | use strict; |
0d80f37e | 4 | use Test::More tests => 24; |
02d1d628 | 5 | |
1c5252ed TC |
6 | use Imager qw/NC/; |
7 | use Imager::Test qw(is_image is_color3); | |
02d1d628 | 8 | |
9982a307 | 9 | sub PI () { 3.14159265358979323846 } |
02d1d628 | 10 | |
40e78f96 TC |
11 | -d "testout" or mkdir "testout"; |
12 | ||
0d80f37e TC |
13 | my @out_files; |
14 | ||
15 | END { | |
16 | unless ($ENV{IMAGER_KEEP_FILES}) { | |
17 | unlink @out_files; | |
18 | rmdir "testout"; | |
19 | } | |
20 | } | |
21 | ||
22 | Imager->open_log(log => "testout/250-polyaa.log"); | |
23 | push @out_files, "testout/250-polyaa.log"; | |
1c5252ed TC |
24 | |
25 | my $red = Imager::Color->new(255,0,0); | |
26 | my $green = Imager::Color->new(0,255,0); | |
27 | my $blue = Imager::Color->new(0,0,255); | |
28 | my $white = Imager::Color->new(255,255,255); | |
0d80f37e | 29 | my $black = Imager::Color->new(0, 0, 0); |
1c5252ed TC |
30 | |
31 | { # artifacts with multiple vertical lobes | |
32 | # https://rt.cpan.org/Ticket/Display.html?id=43518 | |
33 | # previously this would have a full coverage pixel at (0,0) caused | |
34 | # by the (20,0.5) point in the right lobe | |
35 | ||
36 | my @pts = | |
37 | ( | |
38 | [ 0.5, -9 ], | |
39 | [ 10, -9 ], | |
40 | [ 10, 11 ], | |
41 | [ 15, 11 ], | |
42 | [ 15, -9 ], | |
43 | [ 17, -9 ], | |
44 | [ 20, 0.5 ], | |
45 | [ 17, 11 ], | |
46 | [ 0.5, 11 ], | |
47 | ); | |
48 | my $im = Imager->new(xsize => 10, ysize => 2); | |
49 | ok($im->polygon(points => \@pts, | |
50 | color => $white), | |
51 | "draw with inside point"); | |
0d80f37e TC |
52 | ok($im->write(file => "testout/250-poly-inside.ppm"), "save to file"); |
53 | push @out_files, "testout/250-poly-inside.ppm"; | |
1c5252ed TC |
54 | # both scanlines should be the same |
55 | my $line0 = $im->crop(top => 0, height => 1); | |
56 | my $line1 = $im->crop(top => 1, height => 1); | |
57 | is_image($line0, $line1, "both scanlines should be the same"); | |
58 | } | |
02d1d628 | 59 | |
1c5252ed TC |
60 | { # check vertical edges are consistent |
61 | my $im = Imager->new(xsize => 10, ysize => 10); | |
62 | ok($im->polygon(points => [ [ 0.5, 0 ], [ 9.25, 0 ], | |
63 | [ 9.25, 10 ], [ 0.5, 10 ] ], | |
64 | color => $white, | |
65 | aa => 1), | |
66 | "draw polygon with mid pixel vertical edges") | |
67 | or diag $im->errstr; | |
68 | my @line0 = $im->getscanline(y => 0); | |
69 | my $im2 = Imager->new(xsize => 10, ysize => 10); | |
70 | for my $y (0..9) { | |
71 | $im2->setscanline(y => $y, pixels => \@line0); | |
72 | } | |
73 | is_image($im, $im2, "all scan lines should be the same"); | |
74 | is_color3($line0[0], 128, 128, 128, "(0,0) should be 50% coverage"); | |
75 | is_color3($line0[9], 64, 64, 64, "(9,0) should be 25% coverage"); | |
9982a307 AMH |
76 | } |
77 | ||
1c5252ed TC |
78 | { # check horizontal edges are consistent |
79 | my $im = Imager->new(xsize => 10, ysize => 10); | |
80 | ok($im->polygon(points => [ [ 0, 0.5 ], [ 0, 9.25 ], | |
81 | [ 10, 9.25 ], [ 10, 0.5 ] ], | |
82 | color => $white, | |
83 | aa => 1), | |
84 | "draw polygon with mid-pixel horizontal edges"); | |
85 | is_deeply([ $im->getsamples(y => 0, channels => [ 0 ]) ], | |
86 | [ (128) x 10 ], | |
87 | "all of line 0 should be 50% coverage"); | |
88 | is_deeply([ $im->getsamples(y => 9, channels => [ 0 ]) ], | |
89 | [ (64) x 10 ], | |
90 | "all of line 9 should be 25% coverage"); | |
91 | } | |
9982a307 | 92 | |
1c5252ed TC |
93 | { |
94 | my $img = Imager->new(xsize=>20, ysize=>10); | |
95 | my @data = translate(5.5,5, | |
96 | rotate(0, | |
97 | scale(5, 5, | |
98 | get_polygon(n_gon => 5) | |
99 | ) | |
100 | ) | |
101 | ); | |
102 | ||
103 | ||
104 | my ($x, $y) = array_to_refpair(@data); | |
0d80f37e | 105 | ok(Imager::i_poly_aa_m($img->{IMG}, $x, $y, 0, $white), "primitive poly"); |
9982a307 | 106 | |
0d80f37e | 107 | ok($img->write(file=>"testout/250-poly.ppm"), "write to file") |
1c5252ed | 108 | or diag $img->errstr; |
0d80f37e | 109 | push @out_files, "testout/250-poly.ppm"; |
d0e7bfee | 110 | |
1c5252ed TC |
111 | my $zoom = make_zoom($img, 8, \@data, $red); |
112 | ok($zoom, "make zoom of primitive"); | |
0d80f37e TC |
113 | $zoom->write(file=>"testout/250-poly-zoom.ppm") or die $zoom->errstr; |
114 | push @out_files, "testout/250-poly-zoom.ppm"; | |
1c5252ed | 115 | } |
d0e7bfee | 116 | |
1c5252ed TC |
117 | { |
118 | my $img = Imager->new(xsize=>300, ysize=>100); | |
119 | ||
120 | my $good = 1; | |
121 | for my $n (0..55) { | |
122 | my @data = translate(20+20*($n%14),18+20*int($n/14), | |
123 | rotate(15*$n/PI, | |
124 | scale(15, 15, | |
125 | get_polygon('box') | |
126 | ) | |
127 | ) | |
128 | ); | |
129 | my ($x, $y) = array_to_refpair(@data); | |
0d80f37e | 130 | Imager::i_poly_aa_m($img->{IMG}, $x, $y, 0, NC(rand(255), rand(255), rand(255))) |
1c5252ed TC |
131 | or $good = 0; |
132 | } | |
133 | ||
0d80f37e | 134 | $img->write(file=>"testout/250-poly-big.ppm") or die $img->errstr; |
d0e7bfee | 135 | |
1c5252ed | 136 | ok($good, "primitive squares"); |
0d80f37e | 137 | push @out_files, "testout/250-poly-big.ppm"; |
1c5252ed | 138 | } |
fe24d684 | 139 | |
1c5252ed TC |
140 | { |
141 | my $img = Imager->new(xsize => 300, ysize => 300); | |
142 | ok($img -> polygon(color=>$white, | |
143 | points => [ | |
144 | translate(150,150, | |
145 | rotate(45*PI/180, | |
146 | scale(70,70, | |
147 | get_polygon('wavycircle', 32*8, sub { 1.2+1*cos(4*$_) })))) | |
148 | ], | |
149 | ), "method call") | |
150 | or diag $img->errstr(); | |
151 | ||
0d80f37e TC |
152 | $img->write(file=>"testout/250-poly-wave.ppm") or die $img->errstr; |
153 | push @out_files, "testout/250-poly-wave.ppm"; | |
1c5252ed | 154 | } |
fe24d684 | 155 | |
1c5252ed TC |
156 | { |
157 | my $img = Imager->new(xsize=>10,ysize=>6); | |
158 | my @data = translate(165,5, | |
159 | scale(80,80, | |
160 | get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) }))); | |
161 | ||
162 | ok($img -> polygon(color=>$white, | |
fe24d684 AMH |
163 | points => [ |
164 | translate(165,5, | |
165 | scale(80,80, | |
166 | get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) }))) | |
167 | ], | |
1c5252ed TC |
168 | ), "bug check") |
169 | or diag $img->errstr(); | |
fe24d684 | 170 | |
0d80f37e | 171 | make_zoom($img,20,\@data, $blue)->write(file=>"testout/250-poly-wavebug.ppm") or die $img->errstr; |
fe24d684 | 172 | |
0d80f37e | 173 | push @out_files, "testout/250-poly-wavebug.ppm"; |
1c5252ed | 174 | } |
fe24d684 | 175 | |
1c5252ed TC |
176 | { |
177 | my $img = Imager->new(xsize=>300, ysize=>300); | |
178 | ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF', dx=>3 }, | |
43c5dacb TC |
179 | points => [ |
180 | translate(150,150, | |
181 | scale(70,70, | |
182 | get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) }))) | |
183 | ], | |
1c5252ed TC |
184 | ), "poly filled with hatch") |
185 | or diag $img->errstr(); | |
0d80f37e TC |
186 | $img->write(file=>"testout/250-poly-wave_fill.ppm") or die $img->errstr; |
187 | push @out_files, "testout/250-poly-wave_fill.ppm"; | |
1c5252ed | 188 | } |
43c5dacb | 189 | |
1c5252ed TC |
190 | { |
191 | my $img = Imager->new(xsize=>300, ysize=>300, bits=>16); | |
192 | ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF' }, | |
43c5dacb TC |
193 | points => [ |
194 | translate(150,150, | |
195 | scale(70,70, | |
196 | get_polygon('wavycircle', 32*8, sub { 1+1*cos(5*$_) }))) | |
197 | ], | |
1c5252ed TC |
198 | ), "hatched to 16-bit image") |
199 | or diag $img->errstr(); | |
0d80f37e TC |
200 | $img->write(file=>"testout/250-poly-wave_fill16.ppm") or die $img->errstr; |
201 | push @out_files, "testout/250-poly-wave_fill16.ppm"; | |
1c5252ed | 202 | } |
9982a307 | 203 | |
0d80f37e TC |
204 | { |
205 | my $img = Imager->new(xsize => 100, ysize => 100); | |
206 | my $poly = | |
207 | [ | |
208 | [ | |
209 | [ 10, 90, 90, 10 ], | |
210 | [ 10, 10, 90, 90 ], | |
211 | ], | |
212 | [ | |
213 | [ 20, 45, 45, 20 ], | |
214 | [ 20, 20, 80, 80 ], | |
215 | ], | |
216 | [ | |
217 | [ 55, 55, 80, 80 ], | |
218 | [ 20, 80, 80, 20 ], | |
219 | ], | |
220 | ]; | |
221 | ok($img->polypolygon | |
222 | ( | |
223 | points => $poly, | |
224 | filled => 1, | |
225 | color => $white, | |
226 | ), "default polypolygon"); | |
227 | push @out_files, "testout/250-poly-ppeo.ppm"; | |
228 | ok($img->write(file => "testout/250-poly-ppeo.ppm"), | |
229 | "save to file"); | |
230 | my $cmp_eo = Imager->new(xsize => 100, ysize => 100); | |
231 | $cmp_eo->box(filled => 1, color => $white, box => [ 10, 10, 89, 89 ]); | |
232 | $cmp_eo->box(filled => 1, color => $black, box => [ 20, 20, 44, 79 ]); | |
233 | $cmp_eo->box(filled => 1, color => $black, box => [ 55, 20, 79, 79 ]); | |
234 | is_image($img, $cmp_eo, "check even/odd matches"); | |
235 | $img = Imager->new(xsize => 100, ysize => 100); | |
236 | ok($img->polypolygon | |
237 | ( | |
238 | points => $poly, | |
239 | filled => 1, | |
240 | color => $white, | |
241 | mode => "nonzero", | |
242 | ), "default polypolygon"); | |
243 | my $cmp_nz = Imager->new(xsize => 100, ysize => 100); | |
244 | $cmp_nz->box(filled => 1, color => $white, box => [ 10, 10, 89, 89 ]); | |
245 | $cmp_nz->box(filled => 1, color => $black, box => [ 55, 20, 79, 79 ]); | |
246 | is_image($img, $cmp_nz, "check non-zero matches"); | |
247 | push @out_files, "testout/250-poly-ppnz.ppm"; | |
248 | ok($img->write(file => "testout/250-poly-ppnz.ppm"), | |
249 | "save to file"); | |
250 | } | |
9982a307 | 251 | |
0d80f37e TC |
252 | Imager->close_log; |
253 | ||
254 | Imager::malloc_state(); | |
9982a307 | 255 | |
1c5252ed TC |
256 | #initialized in a BEGIN, later |
257 | my %primitives; | |
258 | my %polygens; | |
9982a307 AMH |
259 | |
260 | sub get_polygon { | |
261 | my $name = shift; | |
262 | if (exists $primitives{$name}) { | |
263 | return @{$primitives{$name}}; | |
264 | } | |
265 | ||
266 | if (exists $polygens{$name}) { | |
267 | return $polygens{$name}->(@_); | |
268 | } | |
269 | ||
270 | die "polygon spec: $name unknown\n"; | |
271 | } | |
272 | ||
273 | ||
274 | sub make_zoom { | |
275 | my ($img, $sc, $polydata, $linecolor) = @_; | |
276 | ||
277 | # scale with nearest neighboor sampling | |
278 | my $timg = $img->scale(scalefactor=>$sc, qtype=>'preview'); | |
279 | ||
280 | # draw the grid | |
1c5252ed | 281 | for(my $lx=0; $lx<$timg->getwidth(); $lx+=$sc) { |
9982a307 AMH |
282 | $timg->line(color=>$green, x1=>$lx, x2=>$lx, y1=>0, y2=>$timg->getheight(), antialias=>0); |
283 | } | |
284 | ||
1c5252ed | 285 | for(my $ly=0; $ly<$timg->getheight(); $ly+=$sc) { |
9982a307 AMH |
286 | $timg->line(color=>$green, y1=>$ly, y2=>$ly, x1=>0, x2=>$timg->getwidth(), antialias=>0); |
287 | } | |
288 | my @data = scale($sc, $sc, @$polydata); | |
289 | push(@data, $data[0]); | |
290 | my ($x, $y) = array_to_refpair(@data); | |
291 | ||
292 | $timg->polyline(color=>$linecolor, 'x'=>$x, 'y'=>$y, antialias=>0); | |
293 | return $timg; | |
294 | } | |
295 | ||
296 | # utility functions to manipulate point data | |
297 | ||
298 | sub scale { | |
299 | my ($x, $y, @data) = @_; | |
300 | return map { [ $_->[0]*$x , $_->[1]*$y ] } @data; | |
301 | } | |
302 | ||
303 | sub translate { | |
304 | my ($x, $y, @data) = @_; | |
305 | map { [ $_->[0]+$x , $_->[1]+$y ] } @data; | |
306 | } | |
307 | ||
308 | sub rotate { | |
309 | my ($rad, @data) = @_; | |
310 | map { [ $_->[0]*cos($rad)+$_->[1]*sin($rad) , $_->[1]*cos($rad)-$_->[0]*sin($rad) ] } @data; | |
311 | } | |
312 | ||
313 | sub array_to_refpair { | |
314 | my (@x, @y); | |
315 | for (@_) { | |
316 | push(@x, $_->[0]); | |
317 | push(@y, $_->[1]); | |
318 | } | |
319 | return \@x, \@y; | |
320 | } | |
321 | ||
322 | ||
323 | ||
324 | BEGIN { | |
325 | %primitives = ( | |
326 | box => [ [-0.5,-0.5], [0.5,-0.5], [0.5,0.5], [-0.5,0.5] ], | |
327 | triangle => [ [0,0], [1,0], [1,1] ], | |
328 | ); | |
02d1d628 | 329 | |
9982a307 AMH |
330 | %polygens = ( |
331 | wavycircle => sub { | |
332 | my $numv = shift; | |
333 | my $radfunc = shift; | |
334 | my @radians = map { $_*2*PI/$numv } 0..$numv-1; | |
335 | my @radius = map { $radfunc->($_) } @radians; | |
336 | map { | |
d0e7bfee | 337 | [ $radius[$_] * cos($radians[$_]), $radius[$_] * sin($radians[$_]) ] |
9982a307 AMH |
338 | } 0..$#radians; |
339 | }, | |
340 | n_gon => sub { | |
341 | my $N = shift; | |
342 | map { | |
343 | [ cos($_*2*PI/$N), sin($_*2*PI/$N) ] | |
344 | } 0..$N-1; | |
345 | }, | |
346 | ); | |
347 | } |