\n";
323 |
324 | }
325 |
326 | sub theHTML {
327 | $html;
328 | }
329 |
330 | sub WriteToFile {
331 | my $file = shift || return 0;
332 | my $contents = shift || "";
333 |
334 | open (NEWFILE, ">" . $file) or die "couldn't write to file $file";
335 | print NEWFILE $contents;
336 | close(NEWFILE);
337 |
338 | print "created file $file\n";
339 | }
340 |
341 | sub WriteToBinaryFile {
342 | my $file = shift || return 0;
343 | my $contents = shift || "";
344 |
345 | open (NEWFILE, ">" . $file) or die "couldn't write to file $file";
346 | binmode NEWFILE;
347 | print NEWFILE $contents;
348 | close(NEWFILE);
349 |
350 | print "created file $file\n";
351 | }
352 |
353 | sub genHTMLTable {
354 | my $array_of_arrays = shift;
355 | my $border = shift;
356 | my $html_table;
357 |
358 | $html_table .= "
";
359 | for my $array_of_items (@$array_of_arrays) {
360 | $html_table .= "
";
361 | $html_table .= join("
", @$array_of_items);
362 | $html_table .= "
";
363 | }
364 | $html_table .= "
";
365 | $html_table;
366 | }
367 |
368 |
--------------------------------------------------------------------------------
/t/test_data/frog.xpm:
--------------------------------------------------------------------------------
1 | /* XPM */
2 | static char *magick[] = {
3 | /* columns rows colors chars-per-pixel */
4 | "48 48 256 2",
5 | " c #060604040606",
6 | ". c #080888884040",
7 | "X c #9c9c9e9e4c4c",
8 | "o c #1f1fc7c73c3c",
9 | "O c #47478a8a5151",
10 | "+ c #4747c4c43939",
11 | "@ c #747448482828",
12 | "# c #4747a5a53131",
13 | "$ c #2020d8d8b8b8",
14 | "% c #a4a4c6c66c6c",
15 | "& c #71716a6a4444",
16 | "* c #0c0ca6a61414",
17 | "= c #4848aeae7474",
18 | "- c #c8c896960808",
19 | "; c #040446460c0c",
20 | ": c #4848d8d8a0a0",
21 | "> c #0505e9e9e5e5",
22 | ", c #a4a443431111",
23 | "< c #d4d4a6a67c7c",
24 | "1 c #d0d0a8a82424",
25 | "2 c #6464a2a24444",
26 | "3 c #2929ededd9d9",
27 | "4 c #242484845c5c",
28 | "5 c #4848b3b33737",
29 | "6 c #040427270606",
30 | "7 c #6767c7c75757",
31 | "8 c #a2a254541a1a",
32 | "9 c #eaeaaaaa1414",
33 | "0 c #4747d9d95454",
34 | "q c #4646c2c27878",
35 | "w c #2e2eaaaa2c2c",
36 | "e c #0808f6f6dede",
37 | "r c #4a4ab4b44f4f",
38 | "t c #282805050505",
39 | "y c #4747cfcf3d3d",
40 | "u c #e9e9adad4545",
41 | "i c #2828d6d63636",
42 | "p c #272787872f2f",
43 | "a c #4949e9e9c7c7",
44 | "s c #2626fafaf6f6",
45 | "d c #3737b5b52929",
46 | "f c #717155552727",
47 | "g c #4c4c60604040",
48 | "h c #040418180505",
49 | "j c #e4e48e8e0707",
50 | "k c #a3a353533333",
51 | "l c #3838c6c62828",
52 | "z c #5656c6c63c3c",
53 | "x c #6767adad7373",
54 | "c c #fafab4b41212",
55 | "v c #68688c8c4848",
56 | "b c #292993935959",
57 | "n c #a8a868680808",
58 | "m c #5959b5b55252",
59 | "M c #3c3cb5b55555",
60 | "N c #3333c7c75555",
61 | "B c #b5b54a4a2626",
62 | "V c #26269a9a1c1c",
63 | "C c #e4e49a9a5c5c",
64 | "Z c #4646a9a95454",
65 | "A c #484894943434",
66 | "S c #1818f9f9e9e9",
67 | "D c #e4e4caca4c4c",
68 | "F c #040434340808",
69 | "G c #fafaa9a90606",
70 | "H c #e8e89c9c0606",
71 | "J c #909054541717",
72 | "K c #4747c6c65252",
73 | "L c #a7a745452e2e",
74 | "P c #2929edededed",
75 | "I c #3c3ccece1c1c",
76 | "U c #f9f99a9a0707",
77 | "Y c #1c1ce9e9e9e9",
78 | "T c #36368b8b6666",
79 | "R c #6161e6e6d1d1",
80 | "E c #4646bbbb7474",
81 | "W c #b7b755551111",
82 | "Q c #3737b4b43d3d",
83 | "! c #414175754040",
84 | "~ c #181805050404",
85 | "^ c #9494c2c28484",
86 | "/ c #3939d2d2cfcf",
87 | "( c #10107e7e3030",
88 | ") c #6464cecec9c9",
89 | "_ c #161615150404",
90 | "` c #5656aaaa5a5a",
91 | "' c #ccccdededcdc",
92 | "] c #929263633737",
93 | "[ c #8c8c52522f2f",
94 | "{ c #a3a35c5c1717",
95 | "} c #4949bcbc5454",
96 | "| c #b6b658583939",
97 | " . c #2c2c6e6e2c2c",
98 | ".. c #565685856666",
99 | "X. c #3838fbfbf3f3",
100 | "o. c #e7e7fdfdf9f9",
101 | "O. c #c4c4bebe1414",
102 | "+. c #5757baba3737",
103 | "@. c #373788884444",
104 | "#. c #04041b1b2424",
105 | "$. c #3737c5c53b3b",
106 | "%. c #040428282222",
107 | "&. c #fbfba9a92020",
108 | "*. c #a2a24c4c2b2b",
109 | "=. c #09099a9a3737",
110 | "-. c #8484b2b26868",
111 | ";. c #4646caca3b3b",
112 | ":. c #8a8a44441c1c",
113 | ">. c #6666b9b96c6c",
114 | ",. c #5757bcbc5b5b",
115 | "<. c #3535bbbb3c3c",
116 | "1. c #45458b8b6c6c",
117 | "2. c #6666b6b65b5b",
118 | "3. c #26268b8b7e7e",
119 | "4. c #0606fcfcfafa",
120 | "5. c #a4a45b5b3838",
121 | "6. c #c9c967671919",
122 | "7. c #fafaa0a00808",
123 | "8. c #5757b8b87474",
124 | "9. c #59597a7a3737",
125 | "0. c #0707f1f1f2f2",
126 | "q. c #1515fbfbfbfb",
127 | "w. c #34346e6e5c5c",
128 | "e. c #8f8f43433737",
129 | "r. c #05050c0c0505",
130 | "t. c #4848dbdbdcdc",
131 | "y. c #3939ebebd4d4",
132 | "u. c #4747e6e6e4e4",
133 | "i. c #373798985959",
134 | "p. c #9191fefefcfc",
135 | "a. c #a4a49a9a1c1c",
136 | "s. c #6464e2e26c6c",
137 | "d. c #b8b8b8b83434",
138 | "f. c #2121b9b92525",
139 | "g. c #c4c4a2a24444",
140 | "h. c #ccccc2c29494",
141 | "j. c #acacc2c28484",
142 | "k. c #d3d3fefefbfb",
143 | "l. c #b4b4fefefcfc",
144 | "z. c #a4a4aeae5454",
145 | "x. c #717177773f3f",
146 | "c. c #6f6f9b9b4949",
147 | "v. c #efefbbbb3737",
148 | "b. c #8c8c78785050",
149 | "n. c #464696967070",
150 | "m. c #6060e8e8ecec",
151 | "M. c #6868dcdcb8b8",
152 | "N. c #c6c6a8a84646",
153 | "B. c #a9a964644545",
154 | "V. c #3939d8d83d3d",
155 | "C. c #7474cccc7272",
156 | "Z. c #2020a8a84343",
157 | "A. c #7c7cb2b2acac",
158 | "S. c #262699994040",
159 | "D. c #aeaeb9b95f5f",
160 | "F. c #24241a1a0404",
161 | "G. c #dcdcb2b2c4c4",
162 | "H. c #3939a6a65757",
163 | "J. c #2424bebe6464",
164 | "K. c #b8b8b7b7bcbc",
165 | "L. c #b4b4dede8c8c",
166 | "P. c #3737dfdfd4d4",
167 | "I. c #1010dddddcdc",
168 | "U. c #646465653a3a",
169 | "Y. c #69699e9e6c6c",
170 | "T. c #bcbca8a83c3c",
171 | "R. c #d4d4b9b94141",
172 | "E. c #4444f2f2cccc",
173 | "W. c #6c6c92927c7c",
174 | "Q. c #6161f3f3c4c4",
175 | "!. c #dada9d9d1414",
176 | "~. c #f4f4a2a24040",
177 | "^. c #09097d7d5151",
178 | "/. c #6565abab5959",
179 | "(. c #2a2a97977a7a",
180 | "). c #5959dede6767",
181 | "_. c #5757c8c86c6c",
182 | "`. c #f8f8aaaa4444",
183 | "'. c #5c5c9c9c5c5c",
184 | "]. c #5050f3f3ecec",
185 | "[. c #7878ceceb8b8",
186 | "{. c #2f2f7c7c5050",
187 | "}. c #bcbc72722c2c",
188 | "|. c #3434e4e44848",
189 | " X c #8484aeae4444",
190 | ".X c #5c5caeae4444",
191 | "XX c #717155554444",
192 | "oX c #424279795f5f",
193 | "OX c #94946d6d5151",
194 | "+X c #dcdc9c9c5454",
195 | "@X c #747478786060",
196 | "#X c #191999994040",
197 | "$X c #5757dbdbdada",
198 | "%X c #5c5cf2f2e8e8",
199 | "&X c #3c3ccfcf6464",
200 | "*X c #91914d4d1c1c",
201 | "=X c #2c2c7c7c6969",
202 | "-X c #1c1c8d8d4242",
203 | ";X c #808066664444",
204 | ":X c #7777aeae6f6f",
205 | ">X c #3939ecece8e8",
206 | ",X c #f8f8fcfcfafa",
207 | " q.q.UXs s p.l.k.k.,Xo.,Xo.k.k.o.k.k.k.l.p.t.P q.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.",
265 | "4.4.4.q.> q.I.>Xl.k.o.o.,X,Xk.o.o.o.,Xk.k.l.l.p.P 4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.> 4.4.",
266 | "0.4.4.4.4.q.S S P ].l.k.k.l.l.k.k.l.l.l.p.X.I.0.4.4.4.4.4.4.4.S 4.4.4.4.4.4.4.4.q.4.4.4.4.4.4.4.",
267 | "4.4.4.0.q.e s 0.e q.>Xp.m.m./ u.$Xt.t.>Xs q.4.4.4.4.4.4.4.4.0.q.4.4.4.4.4.4.4.4.UXS s q.e 4.4.0.",
268 | "4.0.4.q.X.>X0Xs S 0.> Y P Y P s Y s s 0.0.0.q.q.4.4.4.4.4.4.4.4.UXq.0.4.4.4.4.4.s P.t./ s 4.4.4.",
269 | "4.4.Y P %.%.eXy.3 S q.q.0.q.4.4.4.0.4.4.4.q.0.4.4.4.4.4.4.4.4.0.UXq.4.4.4.4.S y.P.eX#.%.u.UXq.4.",
270 | "4.UX].6 (.4 4 6 #.$X>XS q.0.4.4.4.4.0.4.4.0.4.4.4.4.4.4.4.0.q.0.4.4.4.4.4.S P.%.6 3.3.3.#.t.S 4.",
271 | "e X.h =XT H.sX@.gX6 / X.> q.4.4.4.0.X.0XP X.UXUXs UXX.0X0XX.UXq.0.4.4.4.S X.6 3.T T 3.zX=XeX$ S ",
272 | "y.F cXn.( -XbX-XE (.F t.S 4.4.4.4.S P.#.#.#.%.%X/ m.#.#.#.#.>XS 4.0.4.e y.%.T oX=X4 T =X=X3.%.y.",
273 | "R 6 4 4 8.#X#X=.=.bX^.eXs > 4.4.S 3 %.o.,X,Xk.#.M.h o.,X,Xk.6 E.0.4.q.X.%.=Xw.O {.Z sXi.cX3.%.SX",
274 | "r.n.(.bX4 1X#XN J.uX(.F y.S 4.s >X6 k.,Xt ~ ,X,Xh k.o. ,X,X#.s e S P.6 1.oX@.# -X( -XH.4 =Xh ",
275 | "r.w.3.= @.@.* N N &XE . F y.s I.$Xh ,X~ G.,X ,Xr.o. ' o. ,Xr.0Xs >X%.gX{.dXp LXQ s.#X. cXKXr.",
276 | "h x VX@.-XM K $.BXf.0 uXcX%.u.].) #.k. K.K.r.,Xr.,Xr.K.K. ,X#.>XP %.(.zX4 sX} HXZXo o &X-Xw. ",
277 | "h VXlXO S.N l y I BXIXAX= 6 6X6 6 h k.o.r.r.k.o.h o.,X ,Xo.6 a y.%.=Xi.i.Q ZX5 AX* i =.q lXr.",
278 | "6 VXT @.Z.f.l l I I o 0 Z 6 F uXM ,.h o.,Xo.k.h '.h o.,X,Xo.6 M 6 6 1.1.p z V + PXAXi i BXS.{.h ",
279 | "h n.9X1XJ.0 l I + IX&XQ F ,.5 $.y K .Xh h h 6 8.,.m h r.h 6 FXK K C.6 dX7 PXPX+ ;.d i 5Xi <.x h ",
280 | "F VX4 #XZ.N + PXjX} F 6 ,.5 + l BXIXd hX5 jX<.<.$.$.} m LXr AXIXl f.$.F LXLXz + IXV.|.5Xo &XZ h ",
281 | "A.#.bXbXZ.N r F 6 F r ,.} 5 <.;.uX} 5 7 5 LX).uXuX} FX7 7 # jXK l y <.).F 6 F F IX|.f.5X|.J.6 [.",
282 | "[.eX^.q = r 6 m z <.BXQ } ,.8.h 6 6 r.r.r.h h h 6 h h r.r.h h h h .X>.Z uXK jXFX6 F $.|.f.N F R ",
283 | ") %.4 -XF 6 2.6 LXjXjX} 6 h h r.r. ~ r. ~ r.r.h h h ,.Z m 6 9XE F <.$.&XF / ",
284 | "6X6 T 6 M jXdX6 h h h r. ~ ~ h ~ t qXt t ~ ~ t qXt t ~ r.r. ~ r.h #.h r.h 8.H.} ; 5 uXF GX",
285 | "GX6 sXh jXQ ,.h r.r. r.~ t r.~ < C u +XiXqX:.~.C u N.h ~ ~ r. r.h ` 7 LXF + uXF GX",
286 | "Y eX=XF r Q ,.Z h h h r. r. < +X~.~.!.n J J ~.v.u g.h. ~ ~ h h xX+.z PXF $.J.eXP ",
287 | "q.P h h m +._.r x r.h F r.h h h r.r. ~ 4XtXD d.R.R.1 1 T.F. r.r.r.h h 6 h X.%.oX6 6 6 m x :X:XO 2.9Xh h h h h h h h h h h 6 h h 6 h h 6 6 PXK K + IXPX+..X6 h _.E F X.UX",
289 | "q.P %.3.x dXm F 6 6 h F 6 m x >.2.7 LX,.jXr ,.,.2.Z Z ,.r m C.LX<.0 Q uXBX;.l ; F Z 8.gXq F 0Xs ",
290 | "q.I.y.F b HX5 PX+ ;.Q M ` 6 h h ` Z uXBX$.$.0 d ;.+ IX0 $.Q Z m + } ,.'.kX6 F IXd AXq q F a E.$ ",
291 | "a Q.Q.a F #Xw V l BX&XK Z h ! O 6 F K <.N + y PXd ;.;.uXuX} ,.,.5 6 h h h .X+ l BX$.H.6 6X6XCXCX",
292 | "N.D.C.: E.; ^._.-XK <._.h .i.r r jXF 6 6 6 6 F F 6 6 6 h 6 6 F F i.oXoX1XuXBXl $.LX6 L.% NXd.O.",
293 | "7.v.D.C.Q.: F 4 b p } H.F O -XZ.K 5 5 2.x {.O {.r 2.>.KX1.sXHXd r ,.h h @.} IXBXjXh % z.R.9 9 c ",
294 | "G 9 u T.pX% h 6 h F @.'.:Xh i.M o BXz # 6 @.b E r jXm 6 i.-X$.i ;.FXHX/.2.2.6 6 h h D.R.!.&.G 7.",
295 | "9 !.u tXt r.9.! /.` h 6 h h @.i.N BXl z 6 sXb -X&XV C.6 @.E BXo $.+ 6 6 h h kXKX2 -.6 r.1 v.j 7.",
296 | "9 u ~ h 9.O ` 1Xr LXK ,.m 6 r.lX= } + +.F 1XsX,.jXjXm 6 H.S.IXy uXjXF H.@.1X9X/.>.` A .6 z FX2.h U. dX-XQ z 6 @.9XO '.2.m 6 -XM y <.m 8.h zX{.T 6 nX@.6 >.h 2 c.h d.",
298 | "t ;X@Xr...oXh Y.Y.h Y.-.r.U.OX {.#XAXQ F h yXr. h h h S.#X;.PX2 h h r.W.g r.9.v 6 nX! 6 9.X _ ",
299 | "XX~ ~ & _ & U.r. & f ;XU.6 E Q + ` ^ XX;X lXT -XK w hXh & & U._ pX~ _ z.h h D.~ ~ N.",
300 | ";XmX[ 5.rX:.MXvXMXvX5.[ [ ;Xb.! @.-XZ.F .Xc. OXMX yX{.F M jX# h T.tXv.DX9 v.1 9 2X- 2X!.9 9 9 ",
301 | "vX@ B.7XL 7X3XJ J { *.k iX[ x.lXh /.2.h r.OX[ MXf _ h ! x h Xa.qXH G G 7.7.c 9 H c 7.7.G U &.",
302 | "8 { 7X*.| *.*.8 k 3XiX*XMXOX@ t t f ~ ~ f MXMXMX5.iX] vX~ ~ @ qXqX!.c G G 7.G G G G 7.7.7.7.U 7.",
303 | "7XW 5.*.:.rXk aX*XJ J iX[ 5.rXe.e.B.:.e.B.rX*.k { J *X3X8 8 }.J DXv.7.7.G 7.U U G 7.7.7.7.U 7.7.",
304 | "L L *.k B.B.*X:.5.8 iXiX[ rXL | | k rX| 5.e.rX[ J n n DX`.H fXfXc &.H G c 7.G G 7.G G G G G 7.&.",
305 | "B k | [ *X*X*.aX5.*.*.k k L L L k e.k , e.aX{ { { 9 fX7.c 7.9 c U 7.&.&.7.7.G 7.7.G G G 9 9 G G ",
306 | "e.L L *.7Xk B 7XB B L B B B B 7Xk iX3X, 6.~.fX7.!.c &.7.7.c 9 H G 7.G 7.H G G 7.G 7.G 9 9 G 9 G ",
307 | "aX8 7X6.JXJX6., B B B B , W W { 8 { 7XW j &.U G G 7.7.7.7.7.c 7.G wX7.7.7.c G G 7.G G G G G c G ",
308 | "H 7.H U 7.7.7.7.7.&.&.7.fX7.9 9 &.fX7.7.G c 7.c G U 7.G 7.7.G c G U G G G 7.H 7.G 7.7.G 7.G G G ",
309 | "c c G G G G G G G G 9 9 c c 9 9 H 7.G G G G G G G U 7.7.7.G G 7.G 7.G U G c 7.&.&.7.7.7.7.7.7.G "
310 | };
311 |
--------------------------------------------------------------------------------
/t/GD.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | use strict;
4 | use warnings;
5 |
6 | use FileHandle;
7 | use FindBin qw($Bin);
8 | use lib "$Bin/../blib/lib","$Bin/../blib/arch","$Bin/../lib";
9 | use constant FONT=>"$Bin/test_data/Generic.ttf";
10 | use constant IMAGE_TESTS => 8;
11 | use Test::More tests => IMAGE_TESTS + 7;
12 | use IO::Dir;
13 |
14 | use_ok('GD',':DEFAULT',':cmp');
15 | use_ok('GD::Simple');
16 |
17 | chdir $Bin || die "Couldn't change to 't' directory: $!";
18 | my $images = './test_data/images';
19 |
20 | my $arg = shift;
21 | write_regression_tests() if (defined $arg && $arg eq '--write');
22 | run_image_regression_tests();
23 | run_round_trip_test();
24 | catch_libgd_error();
25 | test_cve2019_6977();
26 |
27 | exit 0;
28 |
29 | sub write_regression_tests {
30 | # TODO get all the supported image formats dynamically
31 | my @image_types = qw(png gif jpeg tiff wbmp webp heif avif);
32 | if (GD::LIBGD_VERSION() < 2.0302 ) {
33 | # GD 2.3.2 disabled the old GD and GD2 formats by default
34 | unshift @image_types, 'gd2', 'gd';
35 | }
36 | warn "Writing regression files...";
37 | for my $suffix (@image_types) {
38 | my $op = ucfirst $suffix;
39 | $op = 'WBMP' if $suffix eq 'wbmp';
40 | unless (GD::Image->can("newFrom$op")) {
41 | print "# not writing $op regression test: not supported\n";
42 | next;
43 | }
44 | for my $t (1..IMAGE_TESTS) {
45 | my $data = eval "test${t}('$suffix')" or die $@;
46 | write_regression_test($data,$t,$suffix);
47 | }
48 | }
49 | }
50 |
51 | sub write_regression_test {
52 | my ($data,$test,$suffix) = @_;
53 | my $base = "$images/t${test}";
54 | mkdir $base unless -d $base;
55 | my $count = 0;
56 | my $filename = sprintf ("$base/$test-%02d.$suffix",$count);
57 | while (-e $filename) {
58 | $count++;
59 | $filename = sprintf ("$base/$test-%02d.$suffix",$count);
60 | }
61 | open my $fh,'>',$filename or die "$filename: $!";
62 | binmode($fh);
63 | print $fh $data->$suffix;
64 | close $fh or die "$filename: $!";
65 | }
66 |
67 | sub compare {
68 | my ($data,$test,$suffix) = @_;
69 | my @files_to_match = glob("$images/t${test}/*.$suffix");
70 |
71 | my $matched;
72 | for my $file (@files_to_match) {
73 | $matched ||= compare_image($data,$file,$suffix);
74 | }
75 | return $matched;
76 | }
77 |
78 | sub compare_image {
79 | my ($data1,$file,$suffix) = @_;
80 | my $op = ucfirst($suffix);
81 | my $method = "newFrom${op}";
82 | my $data2 = eval {GD::Image->$method($file)} or die $@;
83 | return ! $data1->compare($data2) & GD_CMP_IMAGE();
84 | }
85 |
86 | sub test1 {
87 | my $suffix = shift;
88 | my $im = new GD::Image(300,300);
89 | my($white) = $im->colorAllocate(255, 255, 255);
90 | my($black) = $im->colorAllocate(0, 0, 0);
91 | my($red) = $im->colorAllocate(255, 0, 0);
92 | my($green) = $im->colorAllocate(0,255,0);
93 | my($yellow) = $im->colorAllocate(255,250,205);
94 | my $fn = "./test_data/tile.$suffix";
95 | my $op = ucfirst($suffix);
96 | my $tile = eval "GD::Image->newFrom${op}('$fn')" or die $@;
97 | return unless $tile;
98 | $im->setBrush($tile);
99 | $im->arc(100,100,100,150,0,360,gdBrushed());
100 | $im->setTile($tile);
101 | $im->filledRectangle(150,150,250,250,gdTiled());
102 | $im->rectangle(150,150,250,250,$black);
103 | $im->setStyle($green,$green,$green,gdTransparent(),$red,$red,$red,gdTransparent());
104 | $im->line(0,280,300,280,gdStyled());
105 | return $im;
106 | }
107 |
108 | sub test2 {
109 | my($im) = new GD::Image(300,300);
110 | my($white,$black,$red,$blue,$yellow) = (
111 | $im->colorAllocate(255, 255, 255),
112 | $im->colorAllocate(0, 0, 0),
113 | $im->colorAllocate(255, 0, 0),
114 | $im->colorAllocate(0,0,255),
115 | $im->colorAllocate(255,250,205)
116 | );
117 | my($brush) = new GD::Image(10,10);
118 | $brush->colorAllocate(255,255,255); # white
119 | $brush->colorAllocate(0,0,0); # black
120 | $brush->transparent($white); # white is transparent
121 | $brush->filledRectangle(0,0,5,2,$black); # a black rectangle
122 | $im->setBrush($brush);
123 | $im->arc(100,100,100,150,0,360,gdBrushed());
124 | my($poly) = new GD::Polygon;
125 | $poly->addPt(30,30);
126 | $poly->addPt(100,10);
127 | $poly->addPt(190,290);
128 | $poly->addPt(30,290);
129 | $im->polygon($poly,gdBrushed());
130 | $im->fill(132,62,$blue);
131 | $im->fill(100,70,$red);
132 | $im->fill(40,40,$yellow);
133 | $im->copy($im,150,150,20,20,50,50);
134 | $im->copyResized($im,10,200,20,20,100,100,50,50);
135 | return $im;
136 | }
137 |
138 | sub test3 {
139 | my($im) = new GD::Image(100,50);
140 | my($black,$white,$red,$blue) =
141 | (
142 | $im->colorAllocate(0, 0, 0),
143 | $im->colorAllocate(255, 255, 255),
144 | $im->colorAllocate(255, 0, 0),
145 | $im->colorAllocate(0,0,255)
146 | );
147 | $im->arc(50, 25, 98, 48, 0, 360, $white);
148 | $im->fill(50, 21, $red);
149 | return $im;
150 | }
151 |
152 | sub test4 {
153 | my($im) = new GD::Image(225,180);
154 | my($black,$white,$red,$blue,$yellow) =
155 | ($im->colorAllocate(0, 0, 0),
156 | $im->colorAllocate(255, 255, 255),
157 | $im->colorAllocate(255, 0, 0),
158 | $im->colorAllocate(0,0,255),
159 | $im->colorAllocate(255,250,205)
160 | );
161 | my($poly) = new GD::Polygon;
162 | $poly->addPt(0,50);
163 | $poly->addPt(25,25);
164 | $poly->addPt(50,50);
165 | $im->filledPolygon($poly,$blue);
166 | $poly->offset(100,100);
167 | $im->filledPolygon($poly,$red);
168 | $poly->map(50,50,100,100,10,10,110,60);
169 | $im->filledPolygon($poly,$yellow);
170 | $poly->map($poly->bounds,50,20,80,160);
171 | $im->filledPolygon($poly,$white);
172 | return $im;
173 | }
174 |
175 | sub test5 {
176 | my($im) = new GD::Image(300,300);
177 | my($white,$black,$red,$blue,$yellow) =
178 | (
179 | $im->colorAllocate(255, 255, 255),
180 | $im->colorAllocate(0, 0, 0),
181 | $im->colorAllocate(255, 0, 0),
182 | $im->colorAllocate(0,0,255),
183 | $im->colorAllocate(255,250,205)
184 | );
185 | $im->transparent($white);
186 | my($brush) = new GD::Image(10,10);
187 | $brush->colorAllocate(255,255,255);
188 | $brush->colorAllocate(0,0,0);
189 | $brush->transparent($white);
190 | $brush->filledRectangle(0,0,5,2,$black);
191 | $im->string(gdLargeFont(),150,10,"Hello world!",$red);
192 | $im->string(gdSmallFont(),150,28,"Goodbye cruel world!",$blue);
193 | $im->stringUp(gdTinyFont(),280,250,"I'm climbing the wall!",$black);
194 | $im->charUp(gdMediumBoldFont(),280,280,"Q",$black);
195 | $im->setBrush($brush);
196 | $im->arc(100,100,100,150,0,360,gdBrushed());
197 | my $poly = new GD::Polygon;
198 | $poly->addPt(30,30);
199 | $poly->addPt(100,10);
200 | $poly->addPt(190,290);
201 | $poly->addPt(30,290);
202 | $im->polygon($poly,gdBrushed());
203 | $im->fill(132,62,$blue);
204 | $im->fill(100,70,$red);
205 | $im->fill(40,40,$yellow);
206 | return $im;
207 | }
208 |
209 | sub test6 {
210 | my $dtor = 0.0174533;
211 | my $pi = 3.141592654;
212 | my $xsize = 500; my $ysize = 500; my $scale = 1;
213 | my $x_offset = $xsize/2; my $y_offset = $ysize/2;
214 | my $im = new GD::Image($xsize,$ysize);
215 | my $poly = new GD::Polygon;
216 | my $col_bg = $im->colorAllocate(0,0,0);
217 | my $col_fg = $im->colorAllocate(255,255,0);
218 | my $col_fill = $im->colorAllocate(255,0,0);
219 | my $r_0 = 100; my $theta_0 = 20; my $spring_factor = 30;
220 | for(my $theta=0;$theta<=360;$theta++) {
221 | my $r = $r_0 + $spring_factor*sin(2*$pi*$theta/$theta_0);
222 | my $x = int($r * cos($theta*$dtor))*$scale+$x_offset;
223 | my $y = int($r * sin($theta*$dtor))*$scale+$y_offset;
224 | $poly->addPt($x,$y);
225 | }
226 |
227 | $im->filledPolygon($poly,$col_fill); # Call gdImageFilledPolygon()
228 | return $im;
229 | }
230 |
231 | sub test7 {
232 | my $im = GD::Image->new(400,250);
233 | if (!$im) { printf("Test7: no image");};
234 | my($white,$black,$red,$blue,$yellow) =
235 | (
236 | $im->colorAllocate(255, 255, 255),
237 | $im->colorAllocate(0, 0, 0),
238 | $im->colorAllocate(255, 0, 0),
239 | $im->colorAllocate(0,0,255),
240 | $im->colorAllocate(255,250,205)
241 | );
242 |
243 | # Some TTFs
244 | $im->stringFT($black,FONT,12.0,0.0,20,20,"Hello world!") || warn $@;
245 | $im->stringFT($red,FONT,14.0,0.0,20,80,"Hello world!") || warn $@;
246 | $im->stringFT($blue,FONT,30.0,-0.5,60,100,"Goodbye cruel world!") || warn $@;
247 | return $im;
248 | }
249 |
250 | sub test8 {
251 | my $im = test4();
252 | $im = $im->copyRotate90();
253 | $im = $im->copyFlipHorizontal();
254 | $im = $im->copyTranspose();
255 | $im->rotate180();
256 | $im->flipVertical();
257 | $im = $im->copyReverseTranspose();
258 | $im = $im->copyFlipVertical();
259 | return $im;
260 | }
261 |
262 | sub run_image_regression_tests {
263 | my $default_image_type = 'gd2';
264 | if (!GD::Image->can("newFromGd2") || GD::LIBGD_VERSION() >= 2.0302) {
265 | $default_image_type = 'png';
266 | }
267 | my $suffix = $ENV{GDIMAGETYPE} || $default_image_type;
268 | print STDERR "# Testing gd ".GD::VERSION_STRING()." using $suffix support.\n";
269 | for my $t (1..IMAGE_TESTS) {
270 | my $gd = eval "test${t}('$suffix')";
271 | if (!$gd) {
272 | fail("unable to generate comparison image for test $t with $suffix: $@");
273 | } else {
274 | my $ok = compare($gd,$t,$suffix);
275 | unless ($ok) {
276 | if (($suffix ne 'gd2') or ($t == 7)) {
277 | ok(1, "TODO image comparison test $t $suffix failed (regen with --write)");
278 | } else {
279 | ok($ok, "image comparison test $t $suffix");
280 | }
281 | diag("gd: ",GD::VERSION_STRING(),
282 | ", files: ",join(" ",glob("$images/t${t}/*.$suffix")));
283 | } else {
284 | ok($ok, "image comparison test $t $suffix");
285 | }
286 | }
287 | }
288 | }
289 |
290 | sub run_round_trip_test {
291 | my $image = GD::Image->new(300,300);
292 | $image->colorAllocate(255,255,255);
293 | $image->colorAllocate(0,0,0);
294 | $image->colorAllocate(255,0,0);
295 | $image->rectangle(0,0,300,300,0);
296 | $image->filledRectangle(10,10,50,50,2);
297 | if (GD::Image->can("newFromGd")) {
298 | my $gd = $image->gd;
299 | my $image2 = GD::Image->newFromGdData($gd);
300 | ok(!$image->compare($image2) & GD_CMP_IMAGE(),'round trip gd');
301 | my $gd2 = $image->gd2;
302 | $image2 = GD::Image->newFromGd2Data($gd2);
303 | ok(!$image->compare($image2) & GD_CMP_IMAGE(),'round trip gd2');
304 | }
305 | else {
306 | # GD 2.3.2 disabled the old GD and GD2 formats by default
307 | my $png = $image->png;
308 | my $image2 = GD::Image->newFromPngData($png);
309 | ok(!$image->compare($image2) & GD_CMP_IMAGE(),'round trip png');
310 | my $gif = $image->gif;
311 | $image2 = GD::Image->newFromGifData($gif);
312 | ok(!$image->compare($image2) & GD_CMP_IMAGE(),'round trip gif');
313 | }
314 | }
315 |
316 | sub catch_libgd_error {
317 | diag("ignore corrupt png error messages...");
318 | SKIP: {
319 | skip "No PNG support", 2 unless defined &GD::Image::newFromPng;
320 | my $image = eval { GD::Image->newFromPng("test_data/images/corrupt.png") };
321 | is($image, undef);
322 | ok($@, 'caught corrupt png');
323 | }
324 | }
325 |
326 | sub test_cve2019_6977 {
327 | my $img1 = GD::Image->new(0xfff, 0xfff, 1);
328 | my $img2 = GD::Image->new(0xfff, 0xfff, 0);
329 | $img2->colorAllocate(0, 0, 0);
330 | $img2->setPixel (0, 0, 255);
331 | if (GD::LIBGD_VERSION() >= 2.10) {
332 | $img1->colorMatch ($img2);
333 | }
334 | ok(1, 'survived CVE 2019-6977'); # fails only under valgrind or asan
335 | }
336 |
--------------------------------------------------------------------------------
/lib/GD/Image_pm.PL:
--------------------------------------------------------------------------------
1 | #!perl
2 | use Config;
3 | use File::Basename qw(&basename &dirname);
4 | use Cwd;
5 |
6 | my $DEFINES = '';
7 | my $VERSION = '';
8 | if (open F,".config.cache") {
9 | chomp($DEFINES = );
10 | close F;
11 | }
12 |
13 | my $origdir = cwd;
14 | chdir dirname($0);
15 | my $file = 'Image.pm';
16 |
17 | open OUT,">",$file or die "Can't create $file: $!";
18 |
19 | print "Extracting $file (with variable substitutions)\n";
20 |
21 | print OUT <<"!GROK!THIS!";
22 | # DO NOT EDIT! THIS FILE IS AUTOGENERATED BY $0
23 | !GROK!THIS!
24 |
25 | print OUT << '!NO!SUBS!';
26 | package GD::Image;
27 |
28 | use strict;
29 | use GD;
30 | use Symbol 'gensym','qualify_to_ref';
31 | use vars '$VERSION';
32 | $VERSION = '2.83';
33 |
34 | =head1 NAME
35 |
36 | GD::Image - Image class for the GD image library
37 |
38 | =head1 SYNOPSIS
39 |
40 | See L
41 |
42 | =head1 DESCRIPTION
43 |
44 | Supported Image formats:
45 |
46 | =over 4
47 |
48 | !NO!SUBS!
49 |
50 | print OUT "=item Png\n\n" if $DEFINES =~ /HAVE_PNG/;
51 | print OUT "=item Gif\n\n" if $DEFINES =~ /HAVE_GIF/;
52 | print OUT "=item Gd\n\n=item Gd2\n\n" if $DEFINES =~ /HAVE_GD2/;
53 | print OUT "=item Jpeg\n\n" if $DEFINES =~ /HAVE_JPEG/;
54 | print OUT "=item Tiff\n\n" if $DEFINES =~ /HAVE_TIFF/;
55 | print OUT "=item Xpm\n\n" if $DEFINES =~ /HAVE_XPM/;
56 | print OUT "=item Xbm\n\n" if 1 or $DEFINES =~ /HAVE_XBM/;
57 | print OUT "=item WBMP\n\n" if 1 or $DEFINES =~ /HAVE_WBMP/;
58 | print OUT "=item BMP\n\n" if $DEFINES =~ /HAVE_BMP/;
59 | print OUT "=item GifAnim\n\n" if $DEFINES =~ /HAVE_GIFANIM/;
60 | print OUT "=item Webp\n\n" if $DEFINES =~ /HAVE_WEBP/;
61 | print OUT "=item Heif\n\n" if $DEFINES =~ /HAVE_HEIF/;
62 | print OUT "=item Avif\n\n" if $DEFINES =~ /HAVE_AVIF/;
63 |
64 | print OUT << '!NO!SUBS!';
65 | =back
66 |
67 | Unsupported Image formats:
68 |
69 | =over 4
70 |
71 | !NO!SUBS!
72 |
73 | print OUT "=item Png\n\n" if $DEFINES !~ /HAVE_PNG/;
74 | print OUT "=item Gif\n\n" if $DEFINES !~ /HAVE_GIF/;
75 | print OUT "=item Gd\n\n=item Gd2\n\n" if $DEFINES !~ /HAVE_GD2/;
76 | print OUT "=item Jpeg\n\n" if $DEFINES !~ /HAVE_JPEG/;
77 | print OUT "=item Tiff\n\n" if $DEFINES !~ /HAVE_TIFF/;
78 | print OUT "=item Xpm\n\n" if $DEFINES !~ /HAVE_XPM/;
79 | print OUT "=item GifAnim\n\n" if $DEFINES !~ /HAVE_GIFANIM/;
80 | print OUT "=item Webp\n\n" if $DEFINES !~ /HAVE_WEBP/;
81 | print OUT "=item Heif\n\n" if $DEFINES !~ /HAVE_HEIF/;
82 | print OUT "=item Avif\n\n" if $DEFINES !~ /HAVE_AVIF/;
83 | print OUT "=item BMP\n\n" if $DEFINES !~ /HAVE_BMP/;
84 |
85 | print OUT << '!NO!SUBS!';
86 | =back
87 |
88 | See L
89 |
90 | =head1 AUTHOR
91 |
92 | The GD.pm interface is copyright 1995-2005, Lincoln D. Stein. It is
93 | distributed under the same terms as Perl itself. See the "Artistic
94 | License" in the Perl source code distribution for licensing terms.
95 |
96 | The latest versions of GD.pm are available on CPAN:
97 |
98 | http://www.cpan.org
99 |
100 | =head1 SEE ALSO
101 |
102 | L
103 | L,
104 | L,
105 | L,
106 | L
107 |
108 | =cut
109 |
110 | # Copyright 1995 Lincoln D. Stein. See accompanying README file for
111 | # usage information
112 |
113 | *stringTTF = \&GD::Image::stringFT;
114 |
115 | sub _make_filehandle {
116 | shift; # get rid of class
117 | no strict 'refs';
118 | my $thing = shift;
119 | return $thing if defined(fileno $thing);
120 |
121 | # otherwise try qualifying it into caller's package
122 | my $fh;
123 | {
124 | local $^W = 0; # to avoid uninitialized variable warning from Symbol.pm
125 | my $pkg = caller(2);
126 | $pkg = "main" unless defined $pkg;;
127 | $fh = qualify_to_ref($thing,$pkg);
128 | }
129 | return $fh if defined(fileno $fh);
130 |
131 | # otherwise treat it as a file to open
132 | $fh = gensym;
133 | if (!open($fh,$thing)) {
134 | die "$thing not found: $!";
135 | return undef;
136 | }
137 | return $fh;
138 | }
139 |
140 | sub new {
141 | my $pack = shift;
142 | if (@_ == 1) {
143 | if (my $type = _image_type($_[0])) {
144 | my $method = "newFrom${type}Data";
145 | return unless $pack->can($method);
146 | return $pack->$method($_[0]);
147 | } elsif (-f $_[0] and $_[0] =~ /\.gd$/) {
148 | my $type = 'Gd';
149 | return unless my $fh = $pack->_make_filehandle($_[0]);
150 | my $method = "newFrom${type}";
151 | return unless $pack->can($method);
152 | return $pack->$method($fh);
153 | } elsif (-f $_[0] and $_[0] =~ /\.gd2$/) {
154 | my $type = 'Gd2';
155 | return unless my $fh = $pack->_make_filehandle($_[0]);
156 | my $method = "newFrom${type}";
157 | return unless $pack->can($method);
158 | return $pack->$method($fh);
159 | } elsif (-f $_[0] and $_[0] =~ /\.wbmp$/) {
160 | my $type = 'WBMP';
161 | return unless my $fh = $pack->_make_filehandle($_[0]);
162 | my $method = "newFrom${type}";
163 | return unless $pack->can($method);
164 | return $pack->$method($fh);
165 | } elsif (-f $_[0] and $_[0] =~ /\.xpm$/) {
166 | my $type = 'Xpm';
167 | my $method = "newFrom${type}";
168 | return unless $pack->can($method);
169 | return $pack->$method($_[0]);
170 | }
171 | return unless my $fh = $pack->_make_filehandle($_[0]);
172 | my $magic;
173 | return unless read($fh,$magic,64);
174 | return unless my $type = _image_type($magic);
175 | seek($fh,0,0);
176 | my $method = "newFrom${type}";
177 | if ($type eq 'Xpm') {
178 | return $pack->$method($_[0]);
179 | } else {
180 | return $pack->$method($fh);
181 | }
182 | }
183 | return $pack->_new(@_);
184 | }
185 |
186 | sub newTrueColor {
187 | my $pack = shift;
188 | return $pack->_new(@_, 1);
189 | }
190 |
191 | sub newPalette {
192 | my $pack = shift;
193 | return $pack->_new(@_, 0);
194 | }
195 |
196 | sub ellipse ($$$$$) {
197 | my ($self,$cx,$cy,$width,$height,$color) = @_;
198 | $self->arc($cx,$cy,$width,$height,0,360,$color);
199 | }
200 |
201 | # draws closed polygon with the specified color
202 | sub polygon {
203 | my $self = shift;
204 | my($p,$c) = @_;
205 | $self->openPolygon($p, $c);
206 | $self->line( @{$p->{'points'}->[0]},
207 | @{$p->{'points'}->[$p->{'length'}-1]}, $c);
208 | }
209 |
210 | sub width {
211 | my $self = shift;
212 | my @bounds = $self->getBounds;
213 | $bounds[0];
214 | }
215 |
216 | sub height {
217 | my $self = shift;
218 | my @bounds = $self->getBounds;
219 | $bounds[1];
220 | }
221 |
222 | sub _image_type {
223 | my $data = shift;
224 | my $magic = substr($data,0,4);
225 | return 'Png' if $magic eq "\x89PNG";
226 | return 'Jpeg' if ((substr($data,0,3) eq "\377\330\377") &&
227 | ord(substr($data,3,1)) >= 0xc0);
228 | return 'Gif' if $magic eq "GIF8";
229 | return 'Gd2' if $magic eq "gd2\000";
230 | return 'Tiff' if $magic eq "\x4d\x4d\x00\x2a" or
231 | $magic eq "\x49\x49\x2a\x00" or
232 | $magic eq "IIN1";
233 | return 'Bmp' if $magic eq "BMF\000";
234 | return 'Webp' if $magic eq "RIFF" and substr($data,8,4) eq "WEBP";
235 | if (substr($data,4,4) eq "ftyp") { #possibly ISOBMFF-compliant container like HEIF which us used for AVIF and HEIC
236 | #first 4 bytes (they are now in $magic) must contain 32-bit Big Endian size of the 'ftyp' box (including size field and 'ftyp' mark)
237 | my $boxsize = unpack("N", $magic);
238 | if($boxsize>=16 && ($boxsize & 0x3)==0) { #minimum size of 'ftyp' box is 16 bytes and it must be multiple of 4
239 | #Structure of 'ftyp' box (from offset 8):
240 | # uint32 major_brand;
241 | # uint32 minor_version;
242 | # uint32 compatible_brands[]; to end of the box
243 | my $brand = substr($data,8,4); #major_brand
244 | my %compat;
245 | if($boxsize>16) { #compatible_brands list is not empty
246 | %compat = map {$_=>1} unpack("(A4)*", substr($data,16,$boxsize-16));
247 | }
248 | return 'Avif' if $brand eq 'avif' || $compat{'avif'};
249 | #Consider recognizing 'avis' brand meaning AV1 image sequence
250 |
251 | return 'Heif' if $brand eq 'mif1' || $brand eq 'heic' || $brand eq 'heix' || $compat{'heic'} || $compat{'heix'} || $compat{'mif1'};
252 | #'mif1' stands for 'Multiple Image Format' and is general for the HEIF image container with any codec
253 | #'heic' indicates that HEVC Main Profile is utilized
254 | #'heix' indicates that HEVC Main 10 profile is utilized
255 | #Consider recognizing:
256 | # 'msf1' brand meaning 'Multiple Sequence Format' for general image sequence in HEIF
257 | # 'hevc' brand for HEVC Main Profile sequence
258 | # 'hevx' brand for HEVC Main 10 Profile sequence
259 | }
260 | }
261 | return 'Xpm' if substr($data,0,9) eq "/* XPM */";
262 | return 'Xbm' if substr($data,0,8) eq "#define ";
263 | return;
264 | }
265 |
266 |
267 | sub clone {
268 | croak("Usage: clone(\$image)") unless @_ == 1;
269 | my $self = shift;
270 | my ($x,$y) = $self->getBounds;
271 | my $new = $self->new($x,$y);
272 | return unless $new;
273 | $new->copy($self,0,0,0,0,$x,$y);
274 | return $new;
275 | }
276 |
277 | !NO!SUBS!
278 |
279 | if ($DEFINES =~ /HAVE_PNG/) {
280 | print OUT <<'!NO!SUBS!'
281 | sub newFromPng {
282 | croak("Usage: newFromPng(class,filehandle,[truecolor])") unless @_>=2;
283 | my($class) = shift;
284 | my($f) = shift;
285 | my $fh = $class->_make_filehandle($f);
286 | binmode($fh);
287 | $class->_newFromPng($fh,@_);
288 | }
289 |
290 | !NO!SUBS!
291 | }
292 |
293 | if ($DEFINES =~ /HAVE_GD2/) {
294 | print OUT <<'!NO!SUBS!'
295 | sub newFromGd {
296 | croak("Usage: newFromGd(class,filehandle)") unless @_==2;
297 | my($class,$f) = @_;
298 | my $fh = $class->_make_filehandle($f);
299 | binmode($fh);
300 | $class->_newFromGd($fh);
301 | }
302 |
303 | sub newFromGd2 {
304 | croak("Usage: newFromGd2(class,filehandle)") unless @_==2;
305 | my($class,$f) = @_;
306 | my $fh = $class->_make_filehandle($f);
307 | binmode($fh);
308 | $class->_newFromGd2($fh);
309 | }
310 |
311 | sub newFromGd2Part {
312 | croak("Usage: newFromGd2(class,filehandle,srcX,srcY,width,height)") unless @_==6;
313 | my($class,$f) = splice(@_,0,2);
314 | my $fh = $class->_make_filehandle($f);
315 | binmode($fh);
316 | $class->_newFromGd2Part($fh,@_);
317 | }
318 | !NO!SUBS!
319 | }
320 |
321 | if ($DEFINES =~ /HAVE_JPEG/) {
322 | print OUT <<'!NO!SUBS!'
323 | sub newFromJpeg {
324 | croak("Usage: newFromJpeg(class,filehandle,[truecolor])") unless @_>=2;
325 | my($class) = shift;
326 | my($f) = shift;
327 | my $fh = $class->_make_filehandle($f);
328 | binmode($fh);
329 | $class->_newFromJpeg($fh,@_);
330 | }
331 |
332 | !NO!SUBS!
333 | }
334 |
335 | if ($DEFINES =~ /HAVE_GIF/) {
336 | print OUT <<'!NO!SUBS!'
337 | sub newFromGif {
338 | croak("Usage: newFromGif(class,filehandle)") unless @_==2;
339 | my($class) = shift;
340 | my($f) = shift;
341 | my $fh = $class->_make_filehandle($f);
342 | binmode($fh);
343 | $class->_newFromGif($fh,@_);
344 | }
345 |
346 | !NO!SUBS!
347 | }
348 |
349 | if ($DEFINES =~ /HAVE_TIFF/) {
350 | print OUT <<'!NO!SUBS!'
351 | sub newFromTiff {
352 | croak("Usage: newFromTiff(class,filehandle)") unless @_==2;
353 | my($class,$f) = @_;
354 | my $fh = $class->_make_filehandle($f);
355 | binmode($fh);
356 | $class->_newFromTiff($fh);
357 | }
358 |
359 | !NO!SUBS!
360 | }
361 |
362 | print OUT <<'!NO!SUBS!';
363 | sub newFromXbm {
364 | croak("Usage: newFromXbm(class,filehandle)") unless @_==2;
365 | my($class,$f) = @_;
366 | my $fh = $class->_make_filehandle($f);
367 | binmode($fh);
368 | $class->_newFromXbm($fh);
369 | }
370 |
371 | !NO!SUBS!
372 |
373 | if ($DEFINES =~ /HAVE_WEBP/) {
374 | print OUT <<'!NO!SUBS!'
375 | sub newFromWebp {
376 | croak("Usage: newFromWebp(class,filehandle)") unless @_==2;
377 | my($class,$f) = @_;
378 | my $fh = $class->_make_filehandle($f);
379 | binmode($fh);
380 | $class->_newFromWebp($fh);
381 | }
382 |
383 | !NO!SUBS!
384 | }
385 |
386 | if ($DEFINES =~ /HAVE_HEIF/) {
387 | print OUT <<'!NO!SUBS!'
388 | sub newFromHeif {
389 | croak("Usage: newFromHeif(class,filehandle)") unless @_==2;
390 | my($class,$f) = @_;
391 | my $fh = $class->_make_filehandle($f);
392 | binmode($fh);
393 | $class->_newFromHeif($fh);
394 | }
395 |
396 | !NO!SUBS!
397 | }
398 |
399 | if ($DEFINES =~ /HAVE_AVIF/) {
400 | print OUT <<'!NO!SUBS!'
401 | sub newFromAvif {
402 | croak("Usage: newFromAvif(class,filehandle)") unless @_==2;
403 | my($class,$f) = @_;
404 | my $fh = $class->_make_filehandle($f);
405 | binmode($fh);
406 | $class->_newFromAvif($fh);
407 | }
408 |
409 | !NO!SUBS!
410 | }
411 |
412 | if (1 or $DEFINES =~ /HAVE_WBMP/) {
413 | print OUT <<'!NO!SUBS!';
414 | sub newFromWBMP {
415 | croak("Usage: newFromWBMP(class,filehandle)") unless @_==2;
416 | my($class) = shift;
417 | my($f) = shift;
418 | my $fh = $class->_make_filehandle($f);
419 | binmode($fh);
420 | $class->_newFromWBMP($fh,@_);
421 | }
422 |
423 | !NO!SUBS!
424 | }
425 |
426 | if ($DEFINES =~ /HAVE_BMP/) {
427 | print OUT <<'!NO!SUBS!';
428 | sub newFromBmp {
429 | croak("Usage: newFromBmp(class,filehandle)") unless @_==2;
430 | my($class) = shift;
431 | my($f) = shift;
432 | my $fh = $class->_make_filehandle($f);
433 | binmode($fh);
434 | $class->_newFromBmp($fh,@_);
435 | }
436 |
437 | !NO!SUBS!
438 | }
439 |
440 | print OUT <<'!NO!SUBS!';
441 | # Autoload methods go after __END__, and are processed by the autosplit program.
442 | 1;
443 | __END__
444 | !NO!SUBS!
445 |
446 | close OUT or die "Can't close $file: $!";
447 | chdir $origdir;
448 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | GD.pm -- A perl5 interface to Thomas Boutell's gd library.
2 |
3 | ABSTRACT:
4 |
5 | This is a autoloadable interface module for libgd, a popular library
6 | for creating and manipulating PNG files. With this library you can
7 | create PNG images on the fly or modify existing files. Features
8 | include:
9 |
10 | a. lines, polygons, rectangles and arcs, both filled and unfilled
11 | b. flood fills
12 | c. the use of arbitrary images as brushes and as tiled fill patterns
13 | d. line styling (dashed lines and the like)
14 | e. horizontal and vertical text rendering
15 | f. support for transparency and interlacing
16 | g. support for TrueType font rendering, via libfreetype.
17 | h. support for spline curves, via GD::Polyline
18 | i. support for symbolic font names, such as "helvetica:italic"
19 | j. support for symbolic color names, such as "green", via GD::Simple
20 | k. produces output in png, gif, jpeg, xbm and other formats supported by libgd
21 | l. produces output in svg format via GD::SVG.
22 |
23 | For full information on usage, see the accompanying man and html
24 | documentation. Also check the FAQ at the bottom of this document.
25 |
26 | INSTALLATION:
27 |
28 | 1. Windows users can find a binary PPM package in the repositories at
29 | these sites:
30 |
31 | http://trouchelle.com/perl/ppmrepview.pl
32 | http://www.bribes.org/perl/ppmdir.html
33 |
34 | These packages are not always updated to the most recent version, but
35 | GD is pretty stable and you usually won't miss the bleeding edge
36 | version.
37 |
38 | For Unix/darwin users and those who have a developer's kit installed
39 | on Windows (e.g. cygwin):
40 |
41 | 2. Make sure you have downloaded and installed the following packages:
42 |
43 | a. Perl 5.6.0 or higher:
44 | http://www.perl.com/
45 |
46 | b. The gd graphics library:
47 | http://libgd.org
48 |
49 | c. The PNG graphics library:
50 | http://www.libpng.org/pub/png/libpng.html
51 |
52 | d. The zlib compression library:
53 | http://www.gzip.org/zlib/
54 |
55 | (OPTIONAL)
56 |
57 | e. The FreeType font rendering library for TrueType fonts:
58 | http://www.freetype.org/
59 |
60 | f. The JPEG library, version 6b or later:
61 | ftp://ftp.uu.net/graphics/jpeg/
62 |
63 | g. The XPM library, a standard part of modern X Windows
64 | distributions. If you don't have a modern
65 | version of X, don't try to get XPM working.
66 |
67 | 3. On darwin, you can use these package managers to resolve dependencies and
68 | build libgd:
69 |
70 | i. MacPorts http://www.macports.org/
71 | ii. Homebrew http://mxcl.github.io/homebrew/
72 |
73 | If this module fails to compile and link, you are probably using an
74 | older version of libgd. Symptoms of this problem include errors about
75 | functions not being recognized in the gd.h header file, and undefined
76 | symbols from the linker. If you are having this type of error, please
77 | REMOVE all versions of libgd, gd.h from your system and reinstall
78 | libgd 2.0.28 or higher. Do not contact Lincoln for help until you
79 | have done this.
80 |
81 | Use GD 1.41 for libgd versions 1.8.4 and lower.
82 |
83 | 3. Unpack the tar file:
84 |
85 | zcat GD-2.XX.tar.gz | tar xvf -
86 |
87 | (Where "XX" is the most recent revision number.) This will create
88 | the directory GD-2.XX.
89 |
90 | 4. To compile GD.pm:
91 |
92 | a. cd GD-2.XX
93 | b. perl Makefile.PL
94 | c. make
95 | d. make test
96 | f. sudo make install
97 |
98 | This will create GD.pm and install it into
99 | the system-wide Perl library directory. You'll need root
100 | privileges to do the install step. If you don't have them, see below.
101 |
102 | During step (b), Makefile.PL will look for the program gdlib-config or gdlib.pc
103 | that newer versions of libgd install for you. If this program is not present, the
104 | Makefile.PL script will ask you whether to build support for
105 | JPEG, FreeType and/or XPM image formats. Please answer "y" (the default)
106 | if libgd was built with the feature, and "n" if it was not. Failure
107 | to answer correctly will lead to link errors.
108 |
109 | If, during step (b) you see notes about missing libraries, then this
110 | module will probably not link correctly, even though the warning may say
111 | "probably harmless".
112 |
113 | 5. Before you install GD, you will want to run the regression tests. You
114 | can do this after the "make" step by typing:
115 |
116 | make test
117 |
118 | 6. There are some demos you can run in ext/GD/demos. They print PNG
119 | files to standard output. To view the files, pipe their
120 | output to "display" or "xv" in this way:
121 |
122 | a. cd GD-2.XX/demos
123 | b perl shapes.pl | display -
124 |
125 | You will need a graphics program that can read and display PNG
126 | format. I recommend Image::Magick's display program, available from
127 | ftp://ftp.wizards.dupont.com/pub/ImageMagick/
128 | If you don't have any display programs handy, you can save to a
129 | temporary file and display with recent versions of Netscape or
130 | Internet Explorer.
131 |
132 | 7. A program named fonttest is included in this package under demos. This
133 | generates an image showing all the built-in fonts available. If you have
134 | built libgd with TrueType support, and you have a directory containing
135 | some TrueType fonts, you can create a simple font listing by running
136 | the program truetype_test, also located in demos.
137 |
138 | 8. See demos/gd_example.cgi for an example of how to use GD to create
139 | a picture dynamically with a CGI script. It's intended to be run
140 | under a Web server. To see it work, install it in your server's
141 | cgi-bin/ directory and invoke it from a browser by fetching a URL like:
142 |
143 | http://your.site/cgi-bin/gd_example.cgi
144 |
145 | IF YOU RUN INTO PROBLEMS
146 |
147 | If the make and install all seem to go well but you get errors like
148 | "Fatal error: can't load module GD.so", or "Fatal error: unknown
149 | symbol gdFontSmall" when you try to run a script that uses GD, you may
150 | have problems with dynamic linking. Check whether other
151 | dynamically-linked Perl modules such as POSIX and DB_File run
152 | correctly. If not, then you'll have to link Perl statically, as
153 | described above. Other problems may be fixed by compiling libgd as a
154 | shared library, as described in step (2) of the installation
155 | instructions.
156 |
157 | If you are trying to compile and link GD on a Windows or Macintosh
158 | machine and fail, please verify that you are able to build the Perl
159 | distribution from source code. If you can't do that, then you don't
160 | have the compiler/linker/make tools required for building Perl
161 | modules. You may not even need to do this, as ActiveState and MacPerl
162 | both include precompiled versions of GD.
163 |
164 | If you have problems and can't solve it on your own, post a message to
165 | the newsgroup "comp.lang.perl.modules". There are some systems that
166 | require obscure compiler and linker options in order to compile
167 | correctly, and unfortunately I have a limited number of systems at my
168 | disposal. You're much more likely to get correct answers from the
169 | gurus on the newsgroup than from myself.
170 |
171 | THE GD::SIMPLE LIBRARY
172 |
173 | GD::Simple is a simplified API for GD. It supports turtle graphics, a
174 | unified interface for drawing text, and symbolic color names (like
175 | "green"). Run "perldoc GD::Simple" for information on using it.
176 |
177 | The GD::SVG LIBRARY
178 |
179 | GD::SVG, which is available separately on CPAN, provides a subset of
180 | GD method calls. For this subset, you can create images in SVG
181 | (scalable vector graphics) format.
182 |
183 | THE QUICKDRAW LIBRARY
184 |
185 | This is no longer supported.
186 |
187 | FREQUENTLY ASKED QUESTIONS
188 |
189 | 1. I get a warning about prerequisite Math::Trig not being found
190 |
191 | The version of Math::Trig that comes with Perl version 5.6.0 and
192 | lower has a bug in it that causes it not to be found even when it
193 | is installed. Try running perl -MMath::Trig -e0 from the command
194 | line. If you get no errors, go ahead and install GD. If you get an
195 | error, install Math::Trig from CPAN.
196 |
197 | 2. Why do I get errors about functions not being found when building this module?
198 |
199 | You need libgd (the C library that does all the work) version 2.0.28 or
200 | higher. Older versions will give you errors during GD
201 | installation. Get the latest version from http://libgd.org and install it.
202 | Sometimes just installing the new version of libgd is not enough: you must
203 | remove the old library first. Find the gd.h include file and all libgd files
204 | and remove them from your system.
205 |
206 | 3. Why do I get errors about symbols being undefined when building this module?
207 |
208 | See (1).
209 |
210 | 4. The %! thing doesn't compile at all! I'm getting lots of compile errors!
211 |
212 | Does "make" fail with messages like these?
213 |
214 | GD.xs: In function 'newDynamicCtx':
215 | GD.xs:440: error: 'gdIOCtx' has no member named 'gd_free'
216 | GD.xs: In function 'gd_cloneDim':
217 | GD.xs:460: error: 'struct gdImageStruct' has no member named 'alpha'
218 | GD.xs:460: error: 'struct gdImageStruct' has no member named 'alpha'
219 | GD.xs:466: error: 'struct gdImageStruct' has no member named 'thick'
220 | GD.xs:466: error: 'struct gdImageStruct' has no member named 'thick'
221 |
222 | If so, then you may have an old gd.h include file located somewhere
223 | in your system include path. Please find it and remove it. A typical
224 | location is /usr/include/gd.h. The way to make sure you are removing
225 | the correct gd.h is to run "gdlib-config --cflags" to find out where
226 | the current gd.h lives:
227 |
228 | % gdlib-config --cflags
229 | -I/usr/local/include
230 |
231 | This tells you that /usr/local/include/gd.h is the correct gd.h. Please
232 | find and remove any other gd.h.
233 |
234 | 5. My scripts fail with "Can't locate object method 'png' via package "GD::Image".
235 |
236 | libgd can now be built with support for one or more of the PNG, GIF, XPM or
237 | JPEG formats. If one or more of these formats are not supported by libgd, then
238 | the corresponding GD::Image methods will be unavailable. Unfortunately, many
239 | older scripts assume that the png() method will always be present. You can
240 | work around this issue with code like the following:
241 |
242 | my $image = $gd->can('png') ? $gd->png : $gd->gif;
243 |
244 | or if you prefer eval {}
245 |
246 | my $image = eval {$gd->png} || $gd->gif;
247 |
248 | As of libgd 2.0.33, GIF support is always compiled in, so (for the time being!)
249 | this is a safe fallback.
250 |
251 | 6. Is there a utility to convert X Windows BDF fonts into GD fonts.
252 |
253 | Yes. See the utility bdf2gdfont.pl. Run "bdf2gdfont.pl -h" to get help
254 | on using this.
255 |
256 | 7. Does GD run with Macintosh OS X?
257 |
258 | Yes. GD compiles just fine under OSX. However, you may need to
259 | remove old versions of libgd, libpng, and libz and reinstall the
260 | current versions before you try to install GD.
261 |
262 | 8. Does GD run with Win32 Perl?
263 |
264 | The latest ActiveState binaries for Win32 systems come with GD
265 | already compiled in and ready to go. I don't own any Win32 systems,
266 | and cannot provide you with help in compiling GD from scratch on such
267 | systems. Github actions and appveyor are used to test the windows builds.
268 |
269 | 9. GD won't compile on system XX.
270 |
271 | Because libgd relies on multiple external libraries, GD does as well.
272 | Unfortunately, different systems place their libraries in different
273 | places and sometimes are picky about the order in which libraries
274 | are linked. The best thing to do is to install the latest version of
275 | libgd. Recent versions of libgd contain a gdlib-config utility, which
276 | GD will use to determine which libraries are necessary and in which
277 | order to link them.
278 |
279 | Another thing to be aware of is that some Unix distributions provide
280 | a faulty precompiled version of Perl which is unable to build and
281 | load new C-based modules (like this one). If you are getting errors
282 | like this:
283 |
284 | /arch/auto/GD/GD.so: undefined symbol: SetCPerlObj at ....
285 |
286 | then you may have such a faulty version of Perl. The most reliable
287 | thing to do is to recompile Perl from source code, thereby ensuring
288 | that it is complete.
289 |
290 | 10. When I try to load an XPM file, all I get is blackness!
291 |
292 | The libgd createFromXpm() function works with some XPM files, and
293 | not with others. The problem is buried deep in the libXpm library
294 | somewhere.
295 |
296 | 11. The stringFTCircle() method doesn't work!
297 |
298 | I know. I think this might be a problem in libgd because I have
299 | never gotten it to work as a C program. If you have any insight
300 | into this problem let me know.
301 |
302 | 12. Test XX fails
303 |
304 | The regression tests for GD involve generating images, saving
305 | them as PNG, JPEG or GIF files, and then comparing the files bit-for-bit
306 | to known "correct" files. Sometimes one of the underlying
307 | C libraries such as libz, libpng or libgd is updated, causing
308 | GD to generate an image that is subtly different. These differences
309 | are usually insignificant, such as a reordering of colors in the
310 | color table, but they will call isolated tests to fail. If you
311 | are seeing the great majority of GD tests pass, but one or two
312 | fail, then you are probably seeing the effect of a new library.
313 | Just go ahead and install GD and drop me a note about the problem.
314 |
315 | BUG REPORTS
316 |
317 | Please report bugs, feature requests and propose code changes using
318 | the GitHub repository at https://github.com/lstein/Perl-GD. We do not
319 | check the CPAN RT bug system with any frequency.
320 |
321 | ACKNOWLEDGEMENTS:
322 |
323 | I'd like to thank Jan Pazdziora, Geoff Baysinger, and David Kilzer for
324 | their contributions to the library, as well as Thomas Boutell who
325 | wrote libgd.
326 |
327 | SOURCE CODE AND UPDATES:
328 |
329 | The current version of GD can be found in CPAN. The development
330 | version can be found on GitHub at https://github.com/lstein/Perl-GD.
331 |
332 | AUTHOR and LICENSE
333 |
334 | Copyright 1995-2014 Lincoln Stein
335 | Maintainance taken over by Reini Urban 2017.
336 |
337 | This package and its accompanying libraries is free software; you can
338 | redistribute it and/or modify it under the terms of the GPL (either
339 | version 1, or at your option, any later version) or the Artistic
340 | License 2.0. Refer to LICENSE for the full license text.
341 | package for details.
342 |
343 |
--------------------------------------------------------------------------------
/ChangeLog:
--------------------------------------------------------------------------------
1 | 2.83 * Fix missing PNG regression (RT #153923) on old
2 | systems without the .pc, but gdlib-config. The check was too strict.
3 | Requires now File::Which.
4 | 2.82 * Improve HEIF/AVIF autodetection (RT #153305) by Yuriy Yevtukhov.
5 | * Fix strawberryperl default libgd path (shawnlaffan PR #54)
6 | * Fix AVIF and Webp autodetection in tests (shawnlaffan PR #54)
7 | 2.81 * Change GD::Polygon::transform to match old demos (RT #140043),
8 | and GD::Polyline.
9 | Add GD::Polygon::rotate(cw-radian) helper.
10 | Allow GD::Polygon::scale(2.0).
11 | 2.80 * Fix broken copyTranspose and copyReverseTranspose (RT #153300)
12 | by Yuriy Yevtukhov.
13 | * Add transformation tests
14 | * Fix wrong WBMP name and detection
15 | * Fix wrong filename extension auto-detection for gd,gd2,wbmp
16 | * Fix wrong filename extension auto-detection for xpm,
17 | newFromXpm needs the filename, not handle.
18 | * Fix wrong libgd doc link (PR #52) by Tsuyoshi Watanabe
19 | 2.79 * Improve image type autodetection (RT #153212), add a test
20 | * Fix Avif without Heif config
21 | * Improve gdlib.pc reader for supported library features
22 | 2.78 * Fix Use of uninitialized value $pkg in concatenation warning
23 | (RT #148899 and GH #47). Thanks to ppisar for the analysis.
24 | Adds a new hard Test::NoWarnings test requirement.
25 | 2.77 * add BMP support with libgd 2.1.0, PR #49 by PeterCJ
26 | * don't link to -lXPM without XPM nor X11, GH #45
27 | * rename ANIMGIF feature to GIFANIM
28 | * fix unused variable failure in GH CI, RT #141125 by PhilterPaper
29 | 2.76 * fix broken TIFF and AVIF support, PR #43 by Paul Howarth
30 | * re-enable XBM support (always on)
31 | * provide xbm magic support (a hack, for GD::Graph)
32 | 2.75 * add experimental support for TIFF and RAQM (with freetype)
33 | * improve GD2 tests (GH #42, RT #140856)
34 | * also list the unsupported image formats in the GD::Image pod
35 | * fix copyRotated pod (it rotates CCW) PR #36 by LorenzoTo
36 | * fix GD::Simple->fontMetrics docs and implementation GH #37 by Ben Crowell.
37 | fix lineheight calculation according to the docs. You might need to
38 | fix your code!
39 | * add image methods tiff, webp, heif, avif, and its documentation.
40 | * fix the fix for the poly->transform documentation RT #140043
41 | 2.74 * add experimental support for WEBP, HEIF and AVIF
42 | * document all supported Image formats in the GD::Image lib
43 | * added Github actions (for PR's)
44 | * fix poly->transform documentation RT #140043
45 | * fix GD,GD2 detection and tests RT #139399 (since libgd 2.3.3)
46 | fixed tests by Håkon Hægland.
47 | * POD: Remove indirect object constructors from example code snippet (PR #39)
48 | * patch from Ben Crowell that fixes incorrect behaviour of GD::Simple->fontMetrics
49 | * fix cross-compilation if gdlib.pc has no cflags by Fabrice Fontaine
50 | 2.73 * allow --options override the libgd options. Not recommended.
51 | See GH #33 and RT #130045
52 | 2.72 * fix CVE 2019-6977 colorMatch for older unpatched libgd versions.
53 | This is a severe security problem, an exploitable heap-overflow.
54 | See https://nvd.nist.gov/vuln/detail/CVE-2019-6977
55 | 2.71 * skip Test::Fork on freebsd (GH #25)
56 | 2.70 * fixes for hardened CCFLAGS with -Werror (RT #128167)
57 | 2.69 * little spelling error, GH #29 Xavier Guimard
58 | 2.68 * fix GD::Polygon->clear, RT #124463 Michael Cain
59 | 2.67 * fix thread-safety for GD::Simple %COLORS (#26 melak)
60 | * fix arc start-angle docs, RT #123277 Andrew G Gray
61 | * improve setBrush docs, RT #123194 Andrew G Gray
62 | * improve StringFT docs, RT #123193
63 | * replace MacOSX by darwin, and not by Mac OS X/macOS as suggested
64 | in PR #24
65 | * add GD::Image->_file method as suggested in RT #60488 by Kevin Ryde,
66 | also the helper GD::supportsFileType
67 | 2.66 * throw proper error on newFrom* with not-existing file
68 | * add t/transp.t from RT #40525
69 | * Improve RT #54366 multiple gd.h warning
70 | * better doc for GD::Simple->arc
71 | * fix ANIMGIF with libgd 2.3.0-dev
72 | 2.65 * fix --gdlib_config_path to accept an argument (fperrad)
73 | 2.64 * Update doc for LIBGD_VERSION()
74 | * Fix 5.6.2, which does not have float in its typemap
75 | 2.63 * renamed VERSION() to LIBGD_VERSION(), RT #121307.
76 | It was treated magically by "use GD 2.18"
77 | 2.62 * fixed wrong <5.14 code generated with ExtUtils::Constants
78 | RT #121297. Don't generate const-xs.inc, only when missing.
79 | * add -liconv on hpux also (our pkgconfig parser cannot handle it)
80 | 2.61 * add CONFIGURE_REQUIRES META
81 | * add --gdlib_config_path
82 | * add Image Filters: scatter, pixelate, negate, grayscale, brightness,
83 | contrast, color, selectiveBlur, edgeDetectQuick, gaussianBlur, emboss,
84 | meanRemoval, smooth, copyGaussianBlurred
85 | * add palette methods: createPaletteFromTrueColor,
86 | neuQuant (but discouraged), colorMatch.
87 | * add interpolation methods: copyScale, copyRotateInterpolated,
88 | interpolationMethod.
89 | * add double GD::VERSION
90 | * add all gd.h constants
91 | 2.60 * add missing methods newFromWBMP, newFromXbm,
92 | (RT #68784) and some missing docs
93 | * Add --lib_fontconfig_path, --fcgi options
94 | * rewrote most of the XS code
95 | * cleanup Makefile.PL #20
96 | 2.59 * error on failing libgd calls
97 | * fix colorClosestAlpha, colorAllocateAlpha
98 | * add missing documentation
99 | 2.58 * fix VERSION_STRING for 2.0.x
100 | * honor --lib_gd_path specific gdlib-config
101 | * Loosen the comparison tests with GDIMAGETYPE ne gd2
102 | * Improve gdlib-config parsing (PR #17), esp. with 2.0.34
103 | 2.57 * fix Jpeg magic number detection RT #26146
104 | * fix RGB - HSV roundtrips: RT #120572 by J2N-FORGET
105 | * fix -print-search-dirs errors RT #106265
106 | * co-maint to rurban
107 | * add hv_fetchs, CI smokers
108 | * add GD::VERSION_STRING api
109 | 2.56_03 * add alpha method
110 | * improve option handling
111 | * fix meta data
112 | 2.56_02 * fix feature extraction >= 2.2 [RT #119459]
113 | 2.56_01 * rm Build.PL, fix permissions, fix for missing gdlib-config
114 | 2.56 * Fix Makefile.PL so that it works again.
115 | 2.55 * Great simplification of regression framework ought to fix make test problems.
116 | * Replace ExtUtils::MakeMaker script with Module::Build system
117 | (just in time for Module::Build to be deprecated).
118 | * Remove archaic qd.pl (for creating QuickDraw picts) from distribution.
119 | 2.54 Patch from yurly@unet.net to fix image corruption in rotate180 when image height is odd.
120 | 2.53 Points to Gabor Szabo's GD::Simple tutorial, and fix link to repository.
121 | 2.52 Fix regression tests to run on Ubuntu 12.04 64bit.
122 | 2.51 Fix misleading warning message about location of gd.h file.
123 | 2.50 Fix gdUseFontConfig so that it can be called as a class method.
124 | 2.49 Add GitHub information to README.
125 | 2.48 Fix compile crash on windows and strawberry (https://rt.cpan.org/Public/Bug/Display.html?id=67990).
126 | 2.47 Fix compilation on older perl's without the Newxz macros.
127 | 2.46 Added a basic "use" test for GD::Simple
128 | 2.45 Clarified the GD license. There is now a formal LICENSE file in the package.
129 | 2.44 GD::Group now installed properly.
130 | Quenched compiler warning caused by Newxs() calls.
131 | 2.43 Added "transparent" color to GD::Simple.
132 | Fixed Makefile so that GD/Image.pm depends both on GD/Image.pm.PLS and .config.cache
133 | 2.42 Fixed magic number detection to autodetect certain missed jpeg files (thanks to Mike Walker)
134 | 2.41 Added backend support for grouping features in GD::SVG module.
135 | 2.40 ** Do not use - contains a bug **
136 | 2.39 Makefile.PL will refuse to run if the proper version of libgd is unavailable.
137 | 2.38 Fixed bizarre warning about /usr/include/gd.h != /usr/include/gd.h.
138 | 2.37 GD/Image.pm did not bring in croak() properly, meaning that incorrect error messages are printed out when any of the newFromXXX() calls are made.
139 | 2.36 Instructions on using gdAntiAliased with palette images.
140 | 2.35 Some instructions on installation for Windows users.
141 | Doesn't push libpng onto @LIBS unless png support is requested.
142 | Supports Storable's freeze/thaw via a custom serializer.
143 | Remove "scale redefined" message during compilation of Polyline.
144 | 2.34 Added a check for stray gd.h include files from older installations. If any are
145 | found, Makefile.PL will issue a warning.
146 | Fixed incorrect documentation of GD::Simple->string() method. The method call
147 | *does* move the pen.
148 | 2.33 Added appropriate #ifdefs to allow to compile under version 5.6.0 (due to lack of
149 | threading macros before 5.8).
150 | 2.32 Added a GD::Simple->HSVtoRGB() method.
151 | Documentation fixes from Mark Manning.
152 | Added a clear() method to GD::Polygon to remove all vertices.
153 | 2.31 Fixed GD::Simple->transparent to support symbolic color names.
154 | Made changes that should render the module thread-safe.
155 | Changed newSVpv calls to newSVpvn, in hopes of improving performance.
156 | Added a GD::Simple->HSVtoRGB() method.
157 | Fixed incorrect freeing of user-provided raw data in newFromGdData() and newFromGd2Data()
158 | (this caused segfaults; patch provided by Nigel Sandever)
159 | 2.30 Migrated polyline() support into GD::Simple.
160 | 2.29 Better support for fonts and brushed patterns in GD::Simple
161 | 2.28 Having troubles getting all the modules installed correctly. Should work now.
162 | 2.27 Reworked the way that GD.pm is created at compile time so that CPAN picks up
163 | correct version information. No code changes.
164 | 2.26 CPAN isn't propagating GD, so I'll upload another version
165 | 2.25 Fixed Makefile.PL so that GD::Polyline and GD::Simple are installed (thanks to Guy Albertelli).
166 |
167 | 2.24 Fixed gif/anim gif support so that you can't have animated gif support without
168 | having gif support.
169 |
170 | 2.23 Added patch from Slaven Rezic which makes it possible to call GD constants
171 | in an OO way (without generating warnings), and removes #! from the
172 | top of autogenerated GD.pm
173 | Rewrote tests 11 and 12 - if they continue to randomly fail on various
174 | platforms, they will be removed.
175 | 2.22 Changed the way the gd and gd2 round-trip tests are evaluated. This
176 | might fix test failures that have been reported on some platforms.
177 | 2.21 Regression tests are now functional for versions of libgd compiled
178 | exclusively with PNG, JPEG or GIF support.
179 | 2.20 GD::Image->newFromGdData() and newFromGd2Data() got broken
180 | somewhere along the line. They are now fixed (and
181 | have a regression test).
182 | Added copyRotated() method.
183 | 2.19 Added a HAVE_FTCIRCLE define to handle versions of libgd that do not
184 | have the gdImageStringFTCircle() function.
185 | 2.18 This version needs libgd 2.0.28 or higher.
186 | Fixed documentation bug in synopsis of GD::Simple.
187 | Updated Polyline to version 0.20
188 | 2.17 Added animated GIF patches from Jaakko Hyvätti.
189 | Added dynamic bitmapped font loading support.
190 | Added fontconfig support.
191 | Added a simplified API called GD::Simple.
192 | Added support for kern control and other libgd-based FT improvements.
193 | Fixed a define that caused gif functions to be miscompiled on some platforms.
194 | Documentation fixes.
195 | 2.16 Fixed bug in GIF #IFDEFs pointed out by BZAJAC
196 | Added #IFDEF for WIN32 provided by Randy Kobes
197 | 2.15 Brought back GIF support (requires libgd 2.0.28 or higher).
198 | Takes advantage of gdlib-config support in libgd 2.0.27 or higher.
199 | 2.14 Support for AMD64 libraries.
200 | 2.12 Fixed regression test 10 to succeed when used with
201 | libgd 2.0.22
202 | 2.11 More alpha functions from Cory Watson
203 | 2.10 Suppress CAPI warning.
204 | Warn about Math::Trig warning
205 | 2.09 VMS documentation patch from Martin Zinser
206 | Non-standard library finding path options from Peter Kruty
207 | 2.08 Applied 5.00503 compatibility patch from Mathieu Arnold
208 | New check for JPEG magic tag returned by some digital cameras.
209 | 2.07 Now compatible with (and requires!) libgd 2.0.12.
210 | Added setThickness() method.
211 | Added support for compression level argument to png().
212 | Added support for antialiasing drawing using setAntiAliased() and setAntiAliasedDontBled().
213 | Added extended options to stringFT().
214 | Added filledArc(), ellipse() and filledEllipse() methods.
215 | Added command-line options to Makefile.PL provided by David Eisenberg.
216 | 2.06 Added saveAlpha() and alphaBlending() methods.
217 | 2.05 Alpha methods courtesy Georges Arnould.
218 | 2.041 Added a regression test to detect certain versions of freetype.
219 | 2.04 Removed the patch file since Tom has begun adding his own configure file.
220 | Changed the context member from free to gd_free to allow for compiling.
221 | Fixed the regression tests since the gd-generated images have changed slightly.
222 | 2.03 Skipped so as to remain version number compatible with libgd.
223 | 2.02 Changed Math::Trig version requirement from 0.0 to 1.0 as Perl 5.8
224 | no long recognizes this as a valid version number.
225 | 2.01 Added Math::Trig to the prerequisites because GD::Polyline needs it.
226 | 2.00 Folded in support for gd version 2.0 from Dan Palermo
227 | Folded in support for splines (GD::Polyline) from Dan Harasty.
228 | Removed all GIF support.
229 | 1.43 Added demo of Type1 fonts to truetype demo from Slaven Rezic.
230 | 1.42 Fixed the patch_gd.pl file
231 | Version 2.0 is coming.
232 | 1.39 Fixed FreeType test, at least on some platforms.
233 | Added patches from Stephen Clouse to allow to build on 5.8.0rc1.
234 | 1.35 Patches to support Philip Warner's GIF-reinstating library
235 | maintained at http://www.rime.com.au/gd/
236 | 1.34 Fixed problems that arise when compiling against older versions of
237 | libgd that do not have XPM support.
238 | 1.33 Updated patch file for gd 1.8.4
239 | 1.32 Added support for Tru64 UNIX v5.0
240 | 1.29 Fixed a corrupted .xpm file in the regression suite
241 | (caused test 9 to fail)
242 | 1.28 Added support for gd 1.8.3
243 | 1.27 Fixed strict refs problem on 5.00503 and earlier
244 | 1.26 Brought up to date with libgd 1.8.1
245 | 1.24-1.25 More tweaks to Makefile.PL.
246 | 1.23 Added a bunch more libraries and includes... might or might not port
247 | to other platforms now
248 | 1.22 Fix to Makefile.PL to accomodate linking static libraries.
249 | Added newFromGd2Part() method.
250 | Supports libgd 1.7.1.
251 | 1.21 Slight fix in regression tests so that test 8 doesn't fail when compiled
252 | without TrueType support.
253 | 1.20 Rewritten for libgd 1.6.3
254 | 1.19 Fixed Makefile.PL for better compilation on Windoze machines
255 |
256 |
--------------------------------------------------------------------------------
/lib/GD/Polyline.pm:
--------------------------------------------------------------------------------
1 | ############################################################################
2 | #
3 | # Polyline.pm
4 | #
5 | # Author: Dan Harasty
6 | # Email: harasty@cpan.org
7 | # Version: 0.2
8 | # Date: 2002/08/06
9 | #
10 | # For usage documentation: see POD at end of file
11 | #
12 | # For changes: see "Changes" file included with distribution
13 | #
14 |
15 | use strict;
16 |
17 | package GD::Polyline;
18 |
19 | ############################################################################
20 | #
21 | # GD::Polyline
22 | #
23 | ############################################################################
24 | #
25 | # What's this? A class with nothing but a $VERSION and @ISA?
26 | # Below, this module overrides and adds several modules to
27 | # the parent class, GD::Polygon. Those updated/new methods
28 | # act on polygons and polylines, and sometimes those behaviours
29 | # vary slightly based on whether the object is a polygon or polyline.
30 | #
31 |
32 | use vars qw($VERSION @ISA);
33 | $VERSION = "0.2";
34 | @ISA = qw(GD::Polygon);
35 |
36 |
37 | package GD::Polygon;
38 |
39 | ############################################################################
40 | #
41 | # new methods on GD::Polygon
42 | #
43 | ############################################################################
44 |
45 | use GD;
46 | use Carp 'croak','carp';
47 |
48 | use vars qw($bezSegs $csr);
49 | $bezSegs = 20; # number of bezier segs -- number of segments in each portion of the spline produces by toSpline()
50 | $csr = 1/3; # control seg ratio -- the one possibly user-tunable parameter in the addControlPoints() algorithm
51 |
52 |
53 | sub rotate {
54 | my ($self, $angle, $cx, $cy) = @_;
55 | $self->offset(-$cx,-$cy) if $cx or $cy;
56 | $self->transform(cos($angle),sin($angle),-sin($angle),cos($angle),$cx,$cy);
57 | }
58 |
59 | sub centroid {
60 | my ($self, $scale) = @_;
61 | my ($cx,$cy);
62 | $scale = 1 unless defined $scale;
63 |
64 | map {$cx += $_->[0]; $cy += $_->[1]} $self->vertices();
65 |
66 | $cx *= $scale / $self->length();
67 | $cy *= $scale / $self->length();
68 |
69 | return ($cx, $cy);
70 | }
71 |
72 |
73 | sub segLength {
74 | my $self = shift;
75 | my @points = $self->vertices();
76 |
77 | my ($p1, $p2, @segLengths);
78 |
79 | $p1 = shift @points;
80 |
81 | # put the first vertex on the end to "close" a polygon, but not a polyline
82 | push @points, $p1 unless $self->isa('GD::Polyline');
83 |
84 | while ($p2 = shift @points) {
85 | push @segLengths, _len($p1, $p2);
86 | $p1 = $p2;
87 | }
88 |
89 | return @segLengths if wantarray;
90 |
91 | my $sum;
92 | map {$sum += $_} @segLengths;
93 | return $sum;
94 | }
95 |
96 | sub segAngle {
97 | my $self = shift;
98 | my @points = $self->vertices();
99 |
100 | my ($p1, $p2, @segAngles);
101 |
102 | $p1 = shift @points;
103 |
104 | # put the first vertex on the end to "close" a polygon, but not a polyline
105 | push @points, $p1 unless $self->isa('GD::Polyline');
106 |
107 | while ($p2 = shift @points) {
108 | push @segAngles, _angle_reduce2(_angle($p1, $p2));
109 | $p1 = $p2;
110 | }
111 |
112 | return @segAngles;
113 | }
114 |
115 | sub vertexAngle {
116 | my $self = shift;
117 | my @points = $self->vertices();
118 |
119 | my ($p1, $p2, $p3, @vertexAngle);
120 |
121 | $p1 = $points[$#points]; # last vertex
122 | $p2 = shift @points; # current point -- the first vertex
123 |
124 | # put the first vertex on the end to "close" a polygon, but not a polyline
125 | push @points, $p2 unless $self->isa('GD::Polyline');
126 |
127 | while ($p3 = shift @points) {
128 | push @vertexAngle, _angle_reduce2(_angle($p1, $p2, $p3));
129 | ($p1, $p2) = ($p2, $p3);
130 | }
131 |
132 | $vertexAngle[0] = undef if defined $vertexAngle[0] and $self->isa("GD::Polyline");
133 |
134 | return @vertexAngle if wantarray;
135 |
136 | }
137 |
138 |
139 |
140 | sub toSpline {
141 | my $self = shift;
142 | my @points = $self->vertices();
143 |
144 | # put the first vertex on the end to "close" a polygon, but not a polyline
145 | push @points, [$self->getPt(0)] unless $self->isa('GD::Polyline');
146 |
147 | unless (@points > 1 and @points % 3 == 1) {
148 | carp "Attempt to call toSpline() with invalid set of control points";
149 | return undef;
150 | }
151 |
152 | my ($ap1, $dp1, $dp2, $ap2); # ap = anchor point, dp = director point
153 | $ap1 = shift @points;
154 |
155 | my $bez = new ref($self);
156 |
157 | $bez->addPt(@$ap1);
158 |
159 | while (@points) {
160 | ($dp1, $dp2, $ap2) = splice(@points, 0, 3);
161 |
162 | for (1..$bezSegs) {
163 | my ($t0, $t1, $c1, $c2, $c3, $c4, $x, $y);
164 |
165 | $t1 = $_/$bezSegs;
166 | $t0 = (1 - $t1);
167 |
168 | # possible optimization:
169 | # these coefficient could be calculated just once and
170 | # cached in an array for a given value of $bezSegs
171 |
172 | $c1 = $t0 * $t0 * $t0;
173 | $c2 = 3 * $t0 * $t0 * $t1;
174 | $c3 = 3 * $t0 * $t1 * $t1;
175 | $c4 = $t1 * $t1 * $t1;
176 |
177 | $x = $c1 * $ap1->[0] + $c2 * $dp1->[0] + $c3 * $dp2->[0] + $c4 * $ap2->[0];
178 | $y = $c1 * $ap1->[1] + $c2 * $dp1->[1] + $c3 * $dp2->[1] + $c4 * $ap2->[1];
179 |
180 | $bez->addPt($x, $y);
181 | }
182 |
183 | $ap1 = $ap2;
184 | }
185 |
186 | # remove the last anchor point if this is a polygon -- since it will autoclose without it
187 | $bez->deletePt($bez->length()-1) unless $self->isa('GD::Polyline');
188 |
189 | return $bez;
190 | }
191 |
192 | sub addControlPoints {
193 | my $self = shift;
194 | my @points = $self->vertices();
195 |
196 | unless (@points > 1) {
197 | carp "Attempt to call addControlPoints() with too few vertices in polyline";
198 | return undef;
199 | }
200 |
201 | my $points = scalar(@points);
202 | my @segAngles = $self->segAngle();
203 | my @segLengths = $self->segLength();
204 |
205 | my ($prevLen, $nextLen, $prevAngle, $thisAngle, $nextAngle);
206 | my ($controlSeg, $pt, $ptX, $ptY, @controlSegs);
207 |
208 | # this loop goes about creating polylines -- here called control segments --
209 | # that hold the control points for the final set of control points
210 |
211 | # each control segment has three points, and these are colinear
212 |
213 | # the first and last will ultimately be "director points", and
214 | # the middle point will ultimately be an "anchor point"
215 |
216 | for my $i (0..$#points) {
217 |
218 | $controlSeg = new GD::Polyline;
219 |
220 | $pt = $points[$i];
221 | ($ptX, $ptY) = @$pt;
222 |
223 | if ($self->isa('GD::Polyline') and ($i == 0 or $i == $#points)) {
224 | $controlSeg->addPt($ptX, $ptY); # director point
225 | $controlSeg->addPt($ptX, $ptY); # anchor point
226 | $controlSeg->addPt($ptX, $ptY); # director point
227 | next;
228 | }
229 |
230 | $prevLen = $segLengths[$i-1];
231 | $nextLen = $segLengths[$i];
232 | $prevAngle = $segAngles[$i-1];
233 | $nextAngle = $segAngles[$i];
234 |
235 | # make a control segment with control points (director points)
236 | # before and after the point from the polyline (anchor point)
237 |
238 | $controlSeg->addPt($ptX - $csr * $prevLen, $ptY); # director point
239 | $controlSeg->addPt($ptX , $ptY); # anchor point
240 | $controlSeg->addPt($ptX + $csr * $nextLen, $ptY); # director point
241 |
242 | # note that:
243 | # - the line is parallel to the x-axis, as the points have a common $ptY
244 | # - the points are thus clearly colinear
245 | # - the director point is a distance away from the anchor point in proportion to the length of the segment it faces
246 |
247 | # now, we must come up with a reasonable angle for the control seg
248 | # first, "unwrap" $nextAngle w.r.t. $prevAngle
249 | $nextAngle -= 2*pi() until $nextAngle < $prevAngle + pi();
250 | $nextAngle += 2*pi() until $nextAngle > $prevAngle - pi();
251 | # next, use seg lengths as an inverse weighted average
252 | # to "tip" the control segment toward the *shorter* segment
253 | $thisAngle = ($nextAngle * $prevLen + $prevAngle * $nextLen) / ($prevLen + $nextLen);
254 |
255 | # rotate the control segment to $thisAngle about it's anchor point
256 | $controlSeg->rotate($thisAngle, $ptX, $ptY);
257 |
258 | } continue {
259 | # save the control segment for later
260 | push @controlSegs, $controlSeg;
261 |
262 | }
263 |
264 | # post process
265 |
266 | my $controlPoly = new ref($self);
267 |
268 | # collect all the control segments' points in to a single control poly
269 |
270 | foreach my $cs (@controlSegs) {
271 | foreach my $pt ($cs->vertices()) {
272 | $controlPoly->addPt(@$pt);
273 | }
274 | }
275 |
276 | # final clean up based on poly type
277 |
278 | if ($controlPoly->isa('GD::Polyline')) {
279 | # remove the first and last control point
280 | # since they are director points ...
281 | $controlPoly->deletePt(0);
282 | $controlPoly->deletePt($controlPoly->length()-1);
283 | } else {
284 | # move the first control point to the last control point
285 | # since it is supposed to end with two director points ...
286 | $controlPoly->addPt($controlPoly->getPt(0));
287 | $controlPoly->deletePt(0);
288 | }
289 |
290 | return $controlPoly;
291 | }
292 |
293 |
294 | # The following helper functions are for internal
295 | # use of this module. Input arguments of "points"
296 | # refer to an array ref of two numbers, [$x, $y]
297 | # as is used internally in the GD::Polygon
298 | #
299 | # _len()
300 | # Find the length of a segment, passing in two points.
301 | # Internal function; NOT a class or object method.
302 | #
303 | sub _len {
304 | # my ($p1, $p2) = @_;
305 | # return sqrt(($p2->[0]-$p1->[0])**2 + ($p2->[1]-$p1->[1])**2);
306 | my $pt = _subtract(@_);
307 | return sqrt($pt->[0] ** 2 + $pt->[1] **2);
308 | }
309 |
310 | use Math::Trig;
311 |
312 | # _angle()
313 | # Find the angle of... well, depends on the number of arguments:
314 | # - one point: the angle from x-axis to the point (origin is the center)
315 | # - two points: the angle of the vector defined from point1 to point2
316 | # - three points:
317 | # Internal function; NOT a class or object method.
318 | #
319 | sub _angle {
320 | my ($p1, $p2, $p3) = @_;
321 | my $angle = undef;
322 | if (@_ == 1) {
323 | return atan2($p1->[1], $p1->[0]);
324 | }
325 | if (@_ == 2) {
326 | return _angle(_subtract($p1, $p2));
327 | }
328 | if (@_ == 3) {
329 | return _angle(_subtract($p2, $p3)) - _angle(_subtract($p2, $p1));
330 | }
331 | }
332 |
333 | # _subtract()
334 | # Find the difference of two points; returns a point.
335 | # Internal function; NOT a class or object method.
336 | #
337 | sub _subtract {
338 | my ($p1, $p2) = @_;
339 | # print(_print_point($p2), "-", _print_point($p1), "\n");
340 | return [$p2->[0]-$p1->[0], $p2->[1]-$p1->[1]];
341 | }
342 |
343 | # _print_point()
344 | # Returns a string suitable for displaying the value of a point.
345 | # Internal function; NOT a class or object method.
346 | #
347 | sub _print_point {
348 | my ($p1) = @_;
349 | return "[" . join(", ", @$p1) . "]";
350 | }
351 |
352 | # _angle_reduce1()
353 | # "unwraps" angle to interval -pi < angle <= +pi
354 | # Internal function; NOT a class or object method.
355 | #
356 | sub _angle_reduce1 {
357 | my ($angle) = @_;
358 | $angle += 2 * pi() while $angle <= -pi();
359 | $angle -= 2 * pi() while $angle > pi();
360 | return $angle;
361 | }
362 |
363 | # _angle_reduce2()
364 | # "unwraps" angle to interval 0 <= angle < 2 * pi
365 | # Internal function; NOT a class or object method.
366 | #
367 | sub _angle_reduce2 {
368 | my ($angle) = @_;
369 | $angle += 2 * pi() while $angle < 0;
370 | $angle -= 2 * pi() while $angle >= 2 * pi();
371 | return $angle;
372 | }
373 |
374 | ############################################################################
375 | #
376 | # new methods on GD::Image
377 | #
378 | ############################################################################
379 |
380 | sub GD::Image::polyline {
381 | my $self = shift; # the GD::Image
382 | my $p = shift; # the GD::Polyline (or GD::Polygon)
383 | my $c = shift; # the color
384 |
385 | my @points = $p->vertices();
386 | my $p1 = shift @points;
387 | my $p2;
388 | while ($p2 = shift @points) {
389 | $self->line(@$p1, @$p2, $c);
390 | $p1 = $p2;
391 | }
392 | }
393 |
394 | sub GD::Image::polydraw {
395 | my $self = shift; # the GD::Image
396 | my $p = shift; # the GD::Polyline or GD::Polygon
397 | my $c = shift; # the color
398 |
399 | return $self->polyline($p, $c) if $p->isa('GD::Polyline');
400 | return $self->polygon($p, $c);
401 | }
402 |
403 |
404 | 1;
405 | __END__
406 |
407 | =pod
408 |
409 | =head1 NAME
410 |
411 | GD::Polyline - Polyline object and Polygon utilities (including splines) for use with GD
412 |
413 | =head1 SYNOPSIS
414 |
415 | use GD;
416 | use GD::Polyline;
417 |
418 | # create an image
419 | $image = GD::Image->new (500,300);
420 | $white = $image->colorAllocate(255,255,255);
421 | $black = $image->colorAllocate( 0, 0, 0);
422 | $red = $image->colorAllocate(255, 0, 0);
423 |
424 | # create a new polyline
425 | $polyline = GD::Polyline->new;
426 |
427 | # add some points
428 | $polyline->addPt( 0, 0);
429 | $polyline->addPt( 0,100);
430 | $polyline->addPt( 50,125);
431 | $polyline->addPt(100, 0);
432 |
433 | # polylines can use polygon methods (and vice versa)
434 | $polyline->offset(200,100);
435 |
436 | # rotate 60 degrees, about the centroid
437 | $polyline->rotate(3.14159/3, $polyline->centroid());
438 |
439 | # scale about the centroid
440 | $polyline->scale(1.5, 2, $polyline->centroid());
441 |
442 | # draw the polyline
443 | $image->polydraw($polyline,$black);
444 |
445 | # create a spline, which is also a polyine
446 | $spline = $polyline->addControlPoints->toSpline;
447 | $image->polydraw($spline,$red);
448 |
449 | # output the png
450 | binmode STDOUT;
451 | print $image->png;
452 |
453 | =head1 DESCRIPTION
454 |
455 | B extends the GD module by allowing you to create polylines. Think
456 | of a polyline as "an open polygon", that is, the last vertex is not connected
457 | to the first vertex (unless you expressly add the same value as both points).
458 |
459 | For the remainder of this doc, "polyline" will refer to a GD::Polyline,
460 | "polygon" will refer to a GD::Polygon that is not a polyline, and
461 | "polything" and "$poly" may be either.
462 |
463 | The big feature added to GD by this module is the means
464 | to create splines, which are approximations to curves.
465 |
466 | =head1 The Polyline Object
467 |
468 | GD::Polyline defines the following class:
469 |
470 | =over 5
471 |
472 | =item C
473 |
474 | A polyline object, used for storing lists of vertices prior to
475 | rendering a polyline into an image.
476 |
477 | =item C
478 |
479 | Cnew> I
480 |
481 | Create an empty polyline with no vertices.
482 |
483 | $polyline = GD::Polyline->new;
484 |
485 | $polyline->addPt( 0, 0);
486 | $polyline->addPt( 0,100);
487 | $polyline->addPt( 50,100);
488 | $polyline->addPt(100, 0);
489 |
490 | $image->polydraw($polyline,$black);
491 |
492 | In fact GD::Polyline is a subclass of GD::Polygon,
493 | so all polygon methods (such as B and B)
494 | may be used on polylines.
495 | Some new methods have thus been added to GD::Polygon (such as B)
496 | and a few updated/modified/enhanced (such as B) I.
497 | See section "New or Updated GD::Polygon Methods" for more info.
498 |
499 | =back
500 |
501 | Note that this module is very "young" and should be
502 | considered subject to change in future releases, and/or
503 | possibly folded in to the existing polygon object and/or GD module.
504 |
505 | =head1 Updated Polygon Methods
506 |
507 | The following methods (defined in GD.pm) are OVERRIDDEN if you use this module.
508 |
509 | All effort has been made to provide 100% backward compatibility, but if you
510 | can confirm that has not been achieved, please consider that a bug and let the
511 | the author of Polyline.pm know.
512 |
513 | =over 5
514 |
515 | =item C
516 |
517 | C<$poly-Escale($sx, $sy, $cx, $cy)> I