Hack to prevent crashing when libtiff wants to check the filesize of a file.
[imager.git] / Imager.pm
CommitLineData
02d1d628
AMH
1package Imager;
2
02d1d628
AMH
3use strict;
4use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS);
5use IO::File;
6
7use Imager::Color;
8use Imager::Font;
9
10@EXPORT_OK = qw(
11 init
12 init_log
13 DSO_open
14 DSO_close
15 DSO_funclist
16 DSO_call
dd55acc8 17
02d1d628
AMH
18 load_plugin
19 unload_plugin
dd55acc8 20
02d1d628
AMH
21 i_list_formats
22 i_has_format
dd55acc8 23
02d1d628
AMH
24 i_color_new
25 i_color_set
26 i_color_info
dd55acc8 27
02d1d628
AMH
28 i_img_empty
29 i_img_empty_ch
30 i_img_exorcise
31 i_img_destroy
32
33 i_img_info
34
35 i_img_setmask
36 i_img_getmask
37
38 i_draw
39 i_line_aa
40 i_box
41 i_box_filled
42 i_arc
18063344 43 i_circle_aa
dd55acc8 44
02d1d628
AMH
45 i_bezier_multi
46 i_poly_aa
43c5dacb 47 i_poly_aa_cfill
02d1d628
AMH
48
49 i_copyto
50 i_rubthru
51 i_scaleaxis
52 i_scale_nn
53 i_haar
54 i_count_colors
dd55acc8 55
02d1d628
AMH
56 i_gaussian
57 i_conv
dd55acc8 58
f5991c03 59 i_convert
40eba1ea 60 i_map
dd55acc8 61
02d1d628
AMH
62 i_img_diff
63
64 i_init_fonts
65 i_t1_new
66 i_t1_destroy
67 i_t1_set_aa
68 i_t1_cp
69 i_t1_text
70 i_t1_bbox
71
02d1d628
AMH
72 i_tt_set_aa
73 i_tt_cp
74 i_tt_text
75 i_tt_bbox
76
02d1d628
AMH
77 i_readjpeg_wiol
78 i_writejpeg_wiol
79
80 i_readtiff_wiol
81 i_writetiff_wiol
d2dfdcc9 82 i_writetiff_wiol_faxable
02d1d628 83
790923a4
AMH
84 i_readpng_wiol
85 i_writepng_wiol
02d1d628
AMH
86
87 i_readgif
10461f9a 88 i_readgif_wiol
02d1d628
AMH
89 i_readgif_callback
90 i_writegif
91 i_writegifmc
92 i_writegif_gen
93 i_writegif_callback
94
95 i_readpnm_wiol
067d6bdc 96 i_writeppm_wiol
02d1d628 97
895dbd34
AMH
98 i_readraw_wiol
99 i_writeraw_wiol
02d1d628
AMH
100
101 i_contrast
102 i_hardinvert
103 i_noise
104 i_bumpmap
105 i_postlevels
106 i_mosaic
107 i_watermark
dd55acc8 108
02d1d628
AMH
109 malloc_state
110
111 list_formats
dd55acc8 112
02d1d628
AMH
113 i_gifquant
114
115 newfont
116 newcolor
117 newcolour
118 NC
119 NF
02d1d628
AMH
120);
121
9982a307 122@EXPORT=qw(
02d1d628
AMH
123 init_log
124 i_list_formats
125 i_has_format
126 malloc_state
127 i_color_new
128
129 i_img_empty
130 i_img_empty_ch
131 );
132
133%EXPORT_TAGS=
134 (handy => [qw(
135 newfont
136 newcolor
137 NF
138 NC
139 )],
140 all => [@EXPORT_OK],
141 default => [qw(
142 load_plugin
143 unload_plugin
144 )]);
145
02d1d628
AMH
146BEGIN {
147 require Exporter;
148 require DynaLoader;
149
412e7a35 150 $VERSION = '0.39';
02d1d628
AMH
151 @ISA = qw(Exporter DynaLoader);
152 bootstrap Imager $VERSION;
153}
154
155BEGIN {
156 i_init_fonts(); # Initialize font engines
faa9b3e7 157 Imager::Font::__init();
02d1d628
AMH
158 for(i_list_formats()) { $formats{$_}++; }
159
160 if ($formats{'t1'}) {
161 i_t1_set_aa(1);
162 }
163
faa9b3e7
TC
164 if (!$formats{'t1'} and !$formats{'tt'}
165 && !$formats{'ft2'} && !$formats{'w32'}) {
02d1d628
AMH
166 $fontstate='no font support';
167 }
168
169 %OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
170
171 $DEBUG=0;
172
6607600c
TC
173 # the members of the subhashes under %filters are:
174 # callseq - a list of the parameters to the underlying filter in the
175 # order they are passed
176 # callsub - a code ref that takes a named parameter list and calls the
177 # underlying filter
178 # defaults - a hash of default values
179 # names - defines names for value of given parameters so if the names
180 # field is foo=> { bar=>1 }, and the user supplies "bar" as the
181 # foo parameter, the filter will receive 1 for the foo
182 # parameter
02d1d628
AMH
183 $filters{contrast}={
184 callseq => ['image','intensity'],
185 callsub => sub { my %hsh=@_; i_contrast($hsh{image},$hsh{intensity}); }
186 };
187
188 $filters{noise} ={
189 callseq => ['image', 'amount', 'subtype'],
190 defaults => { amount=>3,subtype=>0 },
191 callsub => sub { my %hsh=@_; i_noise($hsh{image},$hsh{amount},$hsh{subtype}); }
192 };
193
194 $filters{hardinvert} ={
195 callseq => ['image'],
196 defaults => { },
197 callsub => sub { my %hsh=@_; i_hardinvert($hsh{image}); }
198 };
199
200 $filters{autolevels} ={
201 callseq => ['image','lsat','usat','skew'],
202 defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 },
203 callsub => sub { my %hsh=@_; i_autolevels($hsh{image},$hsh{lsat},$hsh{usat},$hsh{skew}); }
204 };
205
206 $filters{turbnoise} ={
207 callseq => ['image'],
208 defaults => { xo=>0.0,yo=>0.0,scale=>10.0 },
209 callsub => sub { my %hsh=@_; i_turbnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{scale}); }
210 };
211
212 $filters{radnoise} ={
213 callseq => ['image'],
214 defaults => { xo=>100,yo=>100,ascale=>17.0,rscale=>0.02 },
215 callsub => sub { my %hsh=@_; i_radnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{rscale},$hsh{ascale}); }
216 };
217
218 $filters{conv} ={
219 callseq => ['image', 'coef'],
220 defaults => { },
221 callsub => sub { my %hsh=@_; i_conv($hsh{image},$hsh{coef}); }
222 };
223
224 $filters{gradgen} ={
225 callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
226 defaults => { },
227 callsub => sub { my %hsh=@_; i_gradgen($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
228 };
229
230 $filters{nearest_color} ={
231 callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
232 defaults => { },
233 callsub => sub { my %hsh=@_; i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
234 };
faa9b3e7
TC
235 $filters{gaussian} = {
236 callseq => [ 'image', 'stddev' ],
237 defaults => { },
238 callsub => sub { my %hsh = @_; i_gaussian($hsh{image}, $hsh{stddev}); },
239 };
d08b8f85
TC
240 $filters{mosaic} =
241 {
242 callseq => [ qw(image size) ],
243 defaults => { size => 20 },
244 callsub => sub { my %hsh = @_; i_mosaic($hsh{image}, $hsh{size}) },
245 };
246 $filters{bumpmap} =
247 {
248 callseq => [ qw(image bump elevation lightx lighty st) ],
249 defaults => { elevation=>0, st=> 2 },
b2778574 250 callsub => sub {
d08b8f85
TC
251 my %hsh = @_;
252 i_bumpmap($hsh{image}, $hsh{bump}{IMG}, $hsh{elevation},
253 $hsh{lightx}, $hsh{lighty}, $hsh{st});
254 },
255 };
b2778574
AMH
256 $filters{bumpmap_complex} =
257 {
258 callseq => [ qw(image bump channel tx ty Lx Ly Lz cd cs n Ia Il Is) ],
259 defaults => {
260 channel => 0,
261 tx => 0,
262 ty => 0,
263 Lx => 0.2,
264 Ly => 0.4,
265 Lz => -1.0,
266 cd => 1.0,
267 cs => 40,
268 n => 1.3,
269 Ia => Imager::Color->new(rgb=>[0,0,0]),
270 Il => Imager::Color->new(rgb=>[255,255,255]),
271 Is => Imager::Color->new(rgb=>[255,255,255]),
272 },
273 callsub => sub {
274 my %hsh = @_;
275 i_bumpmap_complex($hsh{image}, $hsh{bump}{IMG}, $hsh{channel},
276 $hsh{tx}, $hsh{ty}, $hsh{Lx}, $hsh{Ly}, $hsh{Lz},
277 $hsh{cd}, $hsh{cs}, $hsh{n}, $hsh{Ia}, $hsh{Il},
278 $hsh{Is});
279 },
280 };
d08b8f85
TC
281 $filters{postlevels} =
282 {
283 callseq => [ qw(image levels) ],
284 defaults => { levels => 10 },
285 callsub => sub { my %hsh = @_; i_postlevels($hsh{image}, $hsh{levels}); },
286 };
287 $filters{watermark} =
288 {
289 callseq => [ qw(image wmark tx ty pixdiff) ],
290 defaults => { pixdiff=>10, tx=>0, ty=>0 },
291 callsub =>
292 sub {
293 my %hsh = @_;
294 i_watermark($hsh{image}, $hsh{wmark}{IMG}, $hsh{tx}, $hsh{ty},
295 $hsh{pixdiff});
296 },
297 };
6607600c
TC
298 $filters{fountain} =
299 {
300 callseq => [ qw(image xa ya xb yb ftype repeat combine super_sample ssample_param segments) ],
301 names => {
302 ftype => { linear => 0,
303 bilinear => 1,
304 radial => 2,
305 radial_square => 3,
306 revolution => 4,
307 conical => 5 },
308 repeat => { none => 0,
309 sawtooth => 1,
310 triangle => 2,
311 saw_both => 3,
312 tri_both => 4,
313 },
314 super_sample => {
315 none => 0,
316 grid => 1,
317 random => 2,
318 circle => 3,
319 },
efdc2568
TC
320 combine => {
321 none => 0,
322 normal => 1,
323 multiply => 2, mult => 2,
324 dissolve => 3,
325 add => 4,
9d540150 326 subtract => 5, 'sub' => 5,
efdc2568
TC
327 diff => 6,
328 lighten => 7,
329 darken => 8,
330 hue => 9,
331 sat => 10,
332 value => 11,
333 color => 12,
334 },
6607600c
TC
335 },
336 defaults => { ftype => 0, repeat => 0, combine => 0,
337 super_sample => 0, ssample_param => 4,
338 segments=>[
339 [ 0, 0.5, 1,
340 Imager::Color->new(0,0,0),
341 Imager::Color->new(255, 255, 255),
342 0, 0,
343 ],
344 ],
345 },
346 callsub =>
347 sub {
348 my %hsh = @_;
349 i_fountain($hsh{image}, $hsh{xa}, $hsh{ya}, $hsh{xb}, $hsh{yb},
350 $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
351 $hsh{ssample_param}, $hsh{segments});
352 },
353 };
b6381851
TC
354 $filters{unsharpmask} =
355 {
356 callseq => [ qw(image stddev scale) ],
357 defaults => { stddev=>2.0, scale=>1.0 },
358 callsub =>
359 sub {
360 my %hsh = @_;
361 i_unsharp_mask($hsh{image}, $hsh{stddev}, $hsh{scale});
362 },
363 };
02d1d628
AMH
364
365 $FORMATGUESS=\&def_guess_type;
366}
367
368#
369# Non methods
370#
371
372# initlize Imager
373# NOTE: this might be moved to an import override later on
374
375#sub import {
376# my $pack = shift;
377# (look through @_ for special tags, process, and remove them);
378# use Data::Dumper;
379# print Dumper($pack);
380# print Dumper(@_);
381#}
382
383sub init {
384 my %parms=(loglevel=>1,@_);
385 if ($parms{'log'}) {
386 init_log($parms{'log'},$parms{'loglevel'});
387 }
388
389# if ($parms{T1LIB_CONFIG}) { $ENV{T1LIB_CONFIG}=$parms{T1LIB_CONFIG}; }
390# if ( $ENV{T1LIB_CONFIG} and ( $fontstate eq 'missing conf' )) {
391# i_init_fonts();
392# $fontstate='ok';
393# }
394}
395
396END {
397 if ($DEBUG) {
398 print "shutdown code\n";
399 # for(keys %instances) { $instances{$_}->DESTROY(); }
400 malloc_state(); # how do decide if this should be used? -- store something from the import
401 print "Imager exiting\n";
402 }
403}
404
405# Load a filter plugin
406
407sub load_plugin {
408 my ($filename)=@_;
409 my $i;
410 my ($DSO_handle,$str)=DSO_open($filename);
411 if (!defined($DSO_handle)) { $Imager::ERRSTR="Couldn't load plugin '$filename'\n"; return undef; }
412 my %funcs=DSO_funclist($DSO_handle);
413 if ($DEBUG) { print "loading module $filename\n"; $i=0; for(keys %funcs) { printf(" %2d: %s\n",$i++,$_); } }
414 $i=0;
415 for(keys %funcs) { if ($filters{$_}) { $ERRSTR="filter '$_' already exists\n"; DSO_close($DSO_handle); return undef; } }
416
417 $DSOs{$filename}=[$DSO_handle,\%funcs];
418
419 for(keys %funcs) {
420 my $evstr="\$filters{'".$_."'}={".$funcs{$_}.'};';
421 $DEBUG && print "eval string:\n",$evstr,"\n";
422 eval $evstr;
423 print $@ if $@;
424 }
425 return 1;
426}
427
428# Unload a plugin
429
430sub unload_plugin {
431 my ($filename)=@_;
432
433 if (!$DSOs{$filename}) { $ERRSTR="plugin '$filename' not loaded."; return undef; }
434 my ($DSO_handle,$funcref)=@{$DSOs{$filename}};
435 for(keys %{$funcref}) {
436 delete $filters{$_};
437 $DEBUG && print "unloading: $_\n";
438 }
439 my $rc=DSO_close($DSO_handle);
440 if (!defined($rc)) { $ERRSTR="unable to unload plugin '$filename'."; return undef; }
441 return 1;
442}
443
64606cc7
TC
444# take the results of i_error() and make a message out of it
445sub _error_as_msg {
446 return join(": ", map $_->[0], i_errors());
447}
448
3a9a4241
TC
449# this function tries to DWIM for color parameters
450# color objects are used as is
451# simple scalars are simply treated as single parameters to Imager::Color->new
452# hashrefs are treated as named argument lists to Imager::Color->new
453# arrayrefs are treated as list arguments to Imager::Color->new iff any
454# parameter is > 1
455# other arrayrefs are treated as list arguments to Imager::Color::Float
456
457sub _color {
458 my $arg = shift;
459 my $result;
460
461 if (ref $arg) {
462 if (UNIVERSAL::isa($arg, "Imager::Color")
463 || UNIVERSAL::isa($arg, "Imager::Color::Float")) {
464 $result = $arg;
465 }
466 else {
467 if ($arg =~ /^HASH\(/) {
468 $result = Imager::Color->new(%$arg);
469 }
470 elsif ($arg =~ /^ARRAY\(/) {
471 if (grep $_ > 1, @$arg) {
472 $result = Imager::Color->new(@$arg);
473 }
474 else {
475 $result = Imager::Color::Float->new(@$arg);
476 }
477 }
478 else {
479 $Imager::ERRSTR = "Not a color";
480 }
481 }
482 }
483 else {
484 # assume Imager::Color::new knows how to handle it
485 $result = Imager::Color->new($arg);
486 }
487
488 return $result;
489}
490
491
02d1d628
AMH
492#
493# Methods to be called on objects.
494#
495
496# Create a new Imager object takes very few parameters.
497# usually you call this method and then call open from
498# the resulting object
499
500sub new {
501 my $class = shift;
502 my $self ={};
503 my %hsh=@_;
504 bless $self,$class;
505 $self->{IMG}=undef; # Just to indicate what exists
506 $self->{ERRSTR}=undef; #
507 $self->{DEBUG}=$DEBUG;
508 $self->{DEBUG} && print "Initialized Imager\n";
509 if ($hsh{xsize} && $hsh{ysize}) { $self->img_set(%hsh); }
510 return $self;
511}
512
02d1d628
AMH
513# Copy an entire image with no changes
514# - if an image has magic the copy of it will not be magical
515
516sub copy {
517 my $self = shift;
518 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
519
520 my $newcopy=Imager->new();
521 $newcopy->{IMG}=i_img_new();
522 i_copy($newcopy->{IMG},$self->{IMG});
523 return $newcopy;
524}
525
526# Paste a region
527
528sub paste {
529 my $self = shift;
530 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
531 my %input=(left=>0, top=>0, @_);
532 unless($input{img}) {
533 $self->{ERRSTR}="no source image";
534 return;
535 }
536 $input{left}=0 if $input{left} <= 0;
537 $input{top}=0 if $input{top} <= 0;
538 my $src=$input{img};
539 my($r,$b)=i_img_info($src->{IMG});
540
541 i_copyto($self->{IMG}, $src->{IMG},
542 0,0, $r, $b, $input{left}, $input{top});
543 return $self; # What should go here??
544}
545
546# Crop an image - i.e. return a new image that is smaller
547
548sub crop {
549 my $self=shift;
550 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
551 my %hsh=(left=>0,right=>0,top=>0,bottom=>0,@_);
552
553 my ($w,$h,$l,$r,$b,$t)=($self->getwidth(),$self->getheight(),
554 @hsh{qw(left right bottom top)});
555 $l=0 if not defined $l;
556 $t=0 if not defined $t;
299a3866
AMH
557
558 $r||=$l+delete $hsh{'width'} if defined $l and exists $hsh{'width'};
559 $b||=$t+delete $hsh{'height'} if defined $t and exists $hsh{'height'};
560 $l||=$r-delete $hsh{'width'} if defined $r and exists $hsh{'width'};
561 $t||=$b-delete $hsh{'height'} if defined $b and exists $hsh{'height'};
562
02d1d628
AMH
563 $r=$self->getwidth if not defined $r;
564 $b=$self->getheight if not defined $b;
565
566 ($l,$r)=($r,$l) if $l>$r;
567 ($t,$b)=($b,$t) if $t>$b;
568
299a3866
AMH
569 if ($hsh{'width'}) {
570 $l=int(0.5+($w-$hsh{'width'})/2);
571 $r=$l+$hsh{'width'};
02d1d628
AMH
572 } else {
573 $hsh{'width'}=$r-$l;
574 }
299a3866
AMH
575 if ($hsh{'height'}) {
576 $b=int(0.5+($h-$hsh{'height'})/2);
577 $t=$h+$hsh{'height'};
02d1d628
AMH
578 } else {
579 $hsh{'height'}=$b-$t;
580 }
581
582# print "l=$l, r=$r, h=$hsh{'width'}\n";
583# print "t=$t, b=$b, w=$hsh{'height'}\n";
584
299a3866 585 my $dst=Imager->new(xsize=>$hsh{'width'}, ysize=>$hsh{'height'}, channels=>$self->getchannels());
02d1d628
AMH
586
587 i_copyto($dst->{IMG},$self->{IMG},$l,$t,$r,$b,0,0);
588 return $dst;
589}
590
591# Sets an image to a certain size and channel number
592# if there was previously data in the image it is discarded
593
594sub img_set {
595 my $self=shift;
596
faa9b3e7 597 my %hsh=(xsize=>100, ysize=>100, channels=>3, bits=>8, type=>'direct', @_);
02d1d628
AMH
598
599 if (defined($self->{IMG})) {
faa9b3e7
TC
600 # let IIM_DESTROY destroy it, it's possible this image is
601 # referenced from a virtual image (like masked)
602 #i_img_destroy($self->{IMG});
02d1d628
AMH
603 undef($self->{IMG});
604 }
605
faa9b3e7
TC
606 if ($hsh{type} eq 'paletted' || $hsh{type} eq 'pseudo') {
607 $self->{IMG} = i_img_pal_new($hsh{xsize}, $hsh{ysize}, $hsh{channels},
608 $hsh{maxcolors} || 256);
609 }
365ea842
TC
610 elsif ($hsh{bits} eq 'double') {
611 $self->{IMG} = i_img_double_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
612 }
faa9b3e7
TC
613 elsif ($hsh{bits} == 16) {
614 $self->{IMG} = i_img_16_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
615 }
616 else {
617 $self->{IMG}=Imager::ImgRaw::new($hsh{'xsize'}, $hsh{'ysize'},
618 $hsh{'channels'});
619 }
620}
621
622# created a masked version of the current image
623sub masked {
624 my $self = shift;
625
626 $self or return undef;
627 my %opts = (left => 0,
628 top => 0,
629 right => $self->getwidth,
630 bottom => $self->getheight,
631 @_);
632 my $mask = $opts{mask} ? $opts{mask}{IMG} : undef;
633
634 my $result = Imager->new;
635 $result->{IMG} = i_img_masked_new($self->{IMG}, $mask, $opts{left},
636 $opts{top}, $opts{right} - $opts{left},
637 $opts{bottom} - $opts{top});
638 # keep references to the mask and base images so they don't
639 # disappear on us
640 $result->{DEPENDS} = [ $self->{IMG}, $mask ];
641
642 $result;
643}
644
645# convert an RGB image into a paletted image
646sub to_paletted {
647 my $self = shift;
648 my $opts;
649 if (@_ != 1 && !ref $_[0]) {
650 $opts = { @_ };
651 }
652 else {
653 $opts = shift;
654 }
655
656 my $result = Imager->new;
657 $result->{IMG} = i_img_to_pal($self->{IMG}, $opts);
658
659 #print "Type ", i_img_type($result->{IMG}), "\n";
660
661 $result->{IMG} or undef $result;
662
663 return $result;
664}
665
666# convert a paletted (or any image) to an 8-bit/channel RGB images
667sub to_rgb8 {
668 my $self = shift;
669 my $result;
670
671 if ($self->{IMG}) {
672 $result = Imager->new;
673 $result->{IMG} = i_img_to_rgb($self->{IMG})
674 or undef $result;
675 }
676
677 return $result;
678}
679
680sub addcolors {
681 my $self = shift;
682 my %opts = (colors=>[], @_);
683
684 @{$opts{colors}} or return undef;
685
686 $self->{IMG} and i_addcolors($self->{IMG}, @{$opts{colors}});
687}
688
689sub setcolors {
690 my $self = shift;
691 my %opts = (start=>0, colors=>[], @_);
692 @{$opts{colors}} or return undef;
693
694 $self->{IMG} and i_setcolors($self->{IMG}, $opts{start}, @{$opts{colors}});
695}
696
697sub getcolors {
698 my $self = shift;
699 my %opts = @_;
700 if (!exists $opts{start} && !exists $opts{count}) {
701 # get them all
702 $opts{start} = 0;
703 $opts{count} = $self->colorcount;
704 }
705 elsif (!exists $opts{count}) {
706 $opts{count} = 1;
707 }
708 elsif (!exists $opts{start}) {
709 $opts{start} = 0;
710 }
711
712 $self->{IMG} and
713 return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
714}
715
716sub colorcount {
717 i_colorcount($_[0]{IMG});
718}
719
720sub maxcolors {
721 i_maxcolors($_[0]{IMG});
722}
723
724sub findcolor {
725 my $self = shift;
726 my %opts = @_;
727 $opts{color} or return undef;
728
729 $self->{IMG} and i_findcolor($self->{IMG}, $opts{color});
730}
731
732sub bits {
733 my $self = shift;
af3c2450
TC
734 my $bits = $self->{IMG} && i_img_bits($self->{IMG});
735 if ($bits && $bits == length(pack("d", 1)) * 8) {
736 $bits = 'double';
737 }
738 $bits;
faa9b3e7
TC
739}
740
741sub type {
742 my $self = shift;
743 if ($self->{IMG}) {
744 return i_img_type($self->{IMG}) ? "paletted" : "direct";
745 }
746}
747
748sub virtual {
749 my $self = shift;
750 $self->{IMG} and i_img_virtual($self->{IMG});
751}
752
753sub tags {
754 my ($self, %opts) = @_;
755
756 $self->{IMG} or return;
757
758 if (defined $opts{name}) {
759 my @result;
760 my $start = 0;
761 my $found;
762 while (defined($found = i_tags_find($self->{IMG}, $opts{name}, $start))) {
763 push @result, (i_tags_get($self->{IMG}, $found))[1];
764 $start = $found+1;
765 }
766 return wantarray ? @result : $result[0];
767 }
768 elsif (defined $opts{code}) {
769 my @result;
770 my $start = 0;
771 my $found;
772 while (defined($found = i_tags_findn($self->{IMG}, $opts{code}, $start))) {
773 push @result, (i_tags_get($self->{IMG}, $found))[1];
774 $start = $found+1;
775 }
776 return @result;
777 }
778 else {
779 if (wantarray) {
780 return map { [ i_tags_get($self->{IMG}, $_) ] } 0.. i_tags_count($self->{IMG})-1;
781 }
782 else {
783 return i_tags_count($self->{IMG});
784 }
785 }
786}
787
788sub addtag {
789 my $self = shift;
790 my %opts = @_;
791
792 return -1 unless $self->{IMG};
793 if ($opts{name}) {
794 if (defined $opts{value}) {
795 if ($opts{value} =~ /^\d+$/) {
796 # add as a number
797 return i_tags_addn($self->{IMG}, $opts{name}, 0, $opts{value});
798 }
799 else {
800 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{value}, 0);
801 }
802 }
803 elsif (defined $opts{data}) {
804 # force addition as a string
805 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{data}, 0);
806 }
807 else {
808 $self->{ERRSTR} = "No value supplied";
809 return undef;
810 }
811 }
812 elsif ($opts{code}) {
813 if (defined $opts{value}) {
814 if ($opts{value} =~ /^\d+$/) {
815 # add as a number
816 return i_tags_addn($self->{IMG}, $opts{code}, 0, $opts{value});
817 }
818 else {
819 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{value}, 0);
820 }
821 }
822 elsif (defined $opts{data}) {
823 # force addition as a string
824 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{data}, 0);
825 }
826 else {
827 $self->{ERRSTR} = "No value supplied";
828 return undef;
829 }
830 }
831 else {
832 return undef;
833 }
834}
835
836sub deltag {
837 my $self = shift;
838 my %opts = @_;
839
840 return 0 unless $self->{IMG};
841
9d540150
TC
842 if (defined $opts{'index'}) {
843 return i_tags_delete($self->{IMG}, $opts{'index'});
faa9b3e7
TC
844 }
845 elsif (defined $opts{name}) {
846 return i_tags_delbyname($self->{IMG}, $opts{name});
847 }
848 elsif (defined $opts{code}) {
849 return i_tags_delbycode($self->{IMG}, $opts{code});
850 }
851 else {
852 $self->{ERRSTR} = "Need to supply index, name, or code parameter";
853 return 0;
854 }
02d1d628
AMH
855}
856
10461f9a
TC
857my @needseekcb = qw/tiff/;
858my %needseekcb = map { $_, $_ } @needseekcb;
859
860
861sub _get_reader_io {
862 my ($self, $input, $type) = @_;
863
864 if ($input->{fd}) {
865 return io_new_fd($input->{fd});
866 }
867 elsif ($input->{fh}) {
868 my $fd = fileno($input->{fh});
869 unless ($fd) {
870 $self->_set_error("Handle in fh option not opened");
871 return;
872 }
873 return io_new_fd($fd);
874 }
875 elsif ($input->{file}) {
876 my $file = IO::File->new($input->{file}, "r");
877 unless ($file) {
878 $self->_set_error("Could not open $input->{file}: $!");
879 return;
880 }
881 binmode $file;
882 return (io_new_fd(fileno($file)), $file);
883 }
884 elsif ($input->{data}) {
885 return io_new_buffer($input->{data});
886 }
887 elsif ($input->{callback} || $input->{readcb}) {
888 if ($needseekcb{$type} && !$input->{seekcb}) {
889 $self->_set_error("Format $type needs a seekcb parameter");
890 }
891 if ($input->{maxbuffer}) {
892 return io_new_cb($input->{writecb},
893 $input->{callback} || $input->{readcb},
894 $input->{seekcb}, $input->{closecb},
895 $input->{maxbuffer});
896 }
897 else {
898 return io_new_cb($input->{writecb},
899 $input->{callback} || $input->{readcb},
900 $input->{seekcb}, $input->{closecb});
901 }
902 }
903 else {
904 $self->_set_error("file/fd/fh/data/callback parameter missing");
905 return;
906 }
907}
908
909sub _get_writer_io {
910 my ($self, $input, $type) = @_;
911
912 if ($input->{fd}) {
913 return io_new_fd($input->{fd});
914 }
915 elsif ($input->{fh}) {
916 my $fd = fileno($input->{fh});
917 unless ($fd) {
918 $self->_set_error("Handle in fh option not opened");
919 return;
920 }
921 return io_new_fd($fd);
922 }
923 elsif ($input->{file}) {
924 my $fh = new IO::File($input->{file},"w+");
925 unless ($fh) {
926 $self->_set_error("Could not open file $input->{file}: $!");
927 return;
928 }
929 binmode($fh) or die;
930 return (io_new_fd(fileno($fh)), $fh);
931 }
932 elsif ($input->{data}) {
933 return io_new_bufchain();
934 }
935 elsif ($input->{callback} || $input->{writecb}) {
936 if ($input->{maxbuffer}) {
937 return io_new_cb($input->{callback} || $input->{writecb},
938 $input->{readcb},
939 $input->{seekcb}, $input->{closecb},
940 $input->{maxbuffer});
941 }
942 else {
943 return io_new_cb($input->{callback} || $input->{writecb},
944 $input->{readcb},
945 $input->{seekcb}, $input->{closecb});
946 }
947 }
948 else {
949 $self->_set_error("file/fd/fh/data/callback parameter missing");
950 return;
951 }
952}
953
02d1d628
AMH
954# Read an image from file
955
956sub read {
957 my $self = shift;
958 my %input=@_;
02d1d628
AMH
959
960 if (defined($self->{IMG})) {
faa9b3e7
TC
961 # let IIM_DESTROY do the destruction, since the image may be
962 # referenced from elsewhere
963 #i_img_destroy($self->{IMG});
02d1d628
AMH
964 undef($self->{IMG});
965 }
966
02d1d628
AMH
967 # FIXME: Find the format here if not specified
968 # yes the code isn't here yet - next week maybe?
dd55acc8
AMH
969 # Next week? Are you high or something? That comment
970 # has been there for half a year dude.
cf692b64 971 # Look, i just work here, ok?
02d1d628 972
9d540150
TC
973 if (!$input{'type'} and $input{file}) {
974 $input{'type'}=$FORMATGUESS->($input{file});
895dbd34 975 }
10461f9a
TC
976 unless ($input{'type'}) {
977 $self->_set_error('type parameter missing and not possible to guess from extension');
978 return undef;
979 }
9d540150 980 if (!$formats{$input{'type'}}) {
895dbd34
AMH
981 $self->{ERRSTR}='format not supported'; return undef;
982 }
02d1d628 983
10461f9a 984 my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1, rgb=>1, gif=>1);
02d1d628 985
9d540150 986 if ($iolready{$input{'type'}}) {
02d1d628 987 # Setup data source
10461f9a
TC
988 my ($IO, $fh) = $self->_get_reader_io(\%input, $input{'type'})
989 or return;
02d1d628 990
9d540150 991 if ( $input{'type'} eq 'jpeg' ) {
02d1d628 992 ($self->{IMG},$self->{IPTCRAW})=i_readjpeg_wiol( $IO );
895dbd34
AMH
993 if ( !defined($self->{IMG}) ) {
994 $self->{ERRSTR}='unable to read jpeg image'; return undef;
995 }
02d1d628
AMH
996 $self->{DEBUG} && print "loading a jpeg file\n";
997 return $self;
998 }
999
9d540150 1000 if ( $input{'type'} eq 'tiff' ) {
02d1d628 1001 $self->{IMG}=i_readtiff_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
895dbd34 1002 if ( !defined($self->{IMG}) ) {
5bb828f1 1003 $self->{ERRSTR}=$self->_error_as_msg(); return undef;
895dbd34 1004 }
02d1d628
AMH
1005 $self->{DEBUG} && print "loading a tiff file\n";
1006 return $self;
1007 }
1008
9d540150 1009 if ( $input{'type'} eq 'pnm' ) {
02d1d628 1010 $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
895dbd34
AMH
1011 if ( !defined($self->{IMG}) ) {
1012 $self->{ERRSTR}='unable to read pnm image: '._error_as_msg(); return undef;
1013 }
02d1d628
AMH
1014 $self->{DEBUG} && print "loading a pnm file\n";
1015 return $self;
1016 }
1017
9d540150 1018 if ( $input{'type'} eq 'png' ) {
790923a4
AMH
1019 $self->{IMG}=i_readpng_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
1020 if ( !defined($self->{IMG}) ) {
1021 $self->{ERRSTR}='unable to read png image';
1022 return undef;
1023 }
1024 $self->{DEBUG} && print "loading a png file\n";
1025 }
1026
9d540150 1027 if ( $input{'type'} eq 'bmp' ) {
705fd961
TC
1028 $self->{IMG}=i_readbmp_wiol( $IO );
1029 if ( !defined($self->{IMG}) ) {
5bb828f1 1030 $self->{ERRSTR}=$self->_error_as_msg();
705fd961
TC
1031 return undef;
1032 }
1033 $self->{DEBUG} && print "loading a bmp file\n";
1034 }
1035
10461f9a
TC
1036 if ( $input{'type'} eq 'gif' ) {
1037 if ($input{colors} && !ref($input{colors})) {
1038 # must be a reference to a scalar that accepts the colour map
1039 $self->{ERRSTR} = "option 'colors' must be a scalar reference";
1040 return undef;
1041 }
1042 if ($input{colors}) {
1043 my $colors;
1044 ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
1045 if ($colors) {
1046 ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
1047 }
1048 }
1049 else {
1050 $self->{IMG} =i_readgif_wiol( $IO );
1051 }
1052 if ( !defined($self->{IMG}) ) {
1053 $self->{ERRSTR}=$self->_error_as_msg();
1054 return undef;
1055 }
1056 $self->{DEBUG} && print "loading a gif file\n";
1057 }
1058
9d540150 1059 if ( $input{'type'} eq 'tga' ) {
1ec86afa
AMH
1060 $self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
1061 if ( !defined($self->{IMG}) ) {
1062 $self->{ERRSTR}=$self->_error_as_msg();
1ec86afa
AMH
1063 return undef;
1064 }
1065 $self->{DEBUG} && print "loading a tga file\n";
1066 }
1067
737a830c
AMH
1068 if ( $input{'type'} eq 'rgb' ) {
1069 $self->{IMG}=i_readrgb_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
1070 if ( !defined($self->{IMG}) ) {
1071 $self->{ERRSTR}=$self->_error_as_msg();
1072 return undef;
1073 }
1074 $self->{DEBUG} && print "loading a tga file\n";
1075 }
1076
1077
9d540150 1078 if ( $input{'type'} eq 'raw' ) {
895dbd34 1079 my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
02d1d628 1080
895dbd34
AMH
1081 if ( !($params{xsize} && $params{ysize}) ) {
1082 $self->{ERRSTR}='missing xsize or ysize parameter for raw';
1083 return undef;
1084 }
02d1d628 1085
895dbd34
AMH
1086 $self->{IMG} = i_readraw_wiol( $IO,
1087 $params{xsize},
1088 $params{ysize},
1089 $params{datachannels},
1090 $params{storechannels},
1091 $params{interleave});
1092 if ( !defined($self->{IMG}) ) {
1093 $self->{ERRSTR}='unable to read raw image';
1094 return undef;
1095 }
1096 $self->{DEBUG} && print "loading a raw file\n";
1097 }
790923a4 1098
895dbd34 1099 } else {
02d1d628 1100
895dbd34 1101 # Old code for reference while changing the new stuff
02d1d628 1102
9d540150
TC
1103 if (!$input{'type'} and $input{file}) {
1104 $input{'type'}=$FORMATGUESS->($input{file});
895dbd34
AMH
1105 }
1106
9d540150 1107 if (!$input{'type'}) {
895dbd34
AMH
1108 $self->{ERRSTR}='type parameter missing and not possible to guess from extension'; return undef;
1109 }
02d1d628 1110
9d540150 1111 if (!$formats{$input{'type'}}) {
895dbd34 1112 $self->{ERRSTR}='format not supported';
a59ffd27
TC
1113 return undef;
1114 }
895dbd34 1115
10461f9a 1116 my ($fh, $fd);
895dbd34
AMH
1117 if ($input{file}) {
1118 $fh = new IO::File($input{file},"r");
1119 if (!defined $fh) {
1120 $self->{ERRSTR}='Could not open file';
1121 return undef;
a59ffd27 1122 }
895dbd34
AMH
1123 binmode($fh);
1124 $fd = $fh->fileno();
a59ffd27 1125 }
895dbd34
AMH
1126
1127 if ($input{fd}) {
1128 $fd=$input{fd};
1129 }
1130
9d540150 1131 if ( $input{'type'} eq 'gif' ) {
895dbd34
AMH
1132 my $colors;
1133 if ($input{colors} && !ref($input{colors})) {
1134 # must be a reference to a scalar that accepts the colour map
1135 $self->{ERRSTR} = "option 'colors' must be a scalar reference";
1136 return undef;
a59ffd27 1137 }
895dbd34
AMH
1138 if (exists $input{data}) {
1139 if ($input{colors}) {
1140 ($self->{IMG}, $colors) = i_readgif_scalar($input{data});
1141 } else {
1142 $self->{IMG}=i_readgif_scalar($input{data});
1143 }
1144 } else {
1145 if ($input{colors}) {
1146 ($self->{IMG}, $colors) = i_readgif( $fd );
1147 } else {
1148 $self->{IMG} = i_readgif( $fd )
1149 }
a59ffd27 1150 }
895dbd34
AMH
1151 if ($colors) {
1152 # we may or may not change i_readgif to return blessed objects...
1153 ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
1154 }
1155 if ( !defined($self->{IMG}) ) {
1156 $self->{ERRSTR}= 'reading GIF:'._error_as_msg();
1157 return undef;
1158 }
1159 $self->{DEBUG} && print "loading a gif file\n";
dd55acc8 1160 }
02d1d628
AMH
1161 }
1162 return $self;
02d1d628
AMH
1163}
1164
02d1d628 1165# Write an image to file
02d1d628
AMH
1166sub write {
1167 my $self = shift;
febba01f
AMH
1168 my %input=(jpegquality=>75,
1169 gifquant=>'mc',
1170 lmdither=>6.0,
1171 lmfixed=>[],
1172 idstring=>"",
1173 compress=>1,
1174 wierdpack=>0,
4c2d6970 1175 fax_fine=>1, @_);
10461f9a 1176 my $rc;
02d1d628 1177
10461f9a
TC
1178 my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1,
1179 gif=>1 ); # this will be SO MUCH BETTER once they are all in there
02d1d628
AMH
1180
1181 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1182
9d540150
TC
1183 if (!$input{'type'} and $input{file}) {
1184 $input{'type'}=$FORMATGUESS->($input{file});
1185 }
1186 if (!$input{'type'}) {
1187 $self->{ERRSTR}='type parameter missing and not possible to guess from extension';
1188 return undef;
1189 }
02d1d628 1190
9d540150 1191 if (!$formats{$input{'type'}}) { $self->{ERRSTR}='format not supported'; return undef; }
02d1d628 1192
10461f9a
TC
1193 my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
1194 or return undef;
02d1d628 1195
10461f9a 1196 # this conditional is probably obsolete
9d540150 1197 if ($iolready{$input{'type'}}) {
02d1d628 1198
9d540150 1199 if ($input{'type'} eq 'tiff') {
4c2d6970
TC
1200 if (defined $input{class} && $input{class} eq 'fax') {
1201 if (!i_writetiff_wiol_faxable($self->{IMG}, $IO, $input{fax_fine})) {
d2dfdcc9
TC
1202 $self->{ERRSTR}='Could not write to buffer';
1203 return undef;
1204 }
04418ecc 1205 } else {
930c67c8
AMH
1206 if (!i_writetiff_wiol($self->{IMG}, $IO)) {
1207 $self->{ERRSTR}='Could not write to buffer';
1208 return undef;
d2dfdcc9
TC
1209 }
1210 }
9d540150 1211 } elsif ( $input{'type'} eq 'pnm' ) {
04418ecc
AMH
1212 if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
1213 $self->{ERRSTR}='unable to write pnm image';
1214 return undef;
1215 }
1216 $self->{DEBUG} && print "writing a pnm file\n";
9d540150 1217 } elsif ( $input{'type'} eq 'raw' ) {
ec9b9c3e 1218 if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
04418ecc
AMH
1219 $self->{ERRSTR}='unable to write raw image';
1220 return undef;
1221 }
1222 $self->{DEBUG} && print "writing a raw file\n";
9d540150 1223 } elsif ( $input{'type'} eq 'png' ) {
ec9b9c3e
AMH
1224 if ( !i_writepng_wiol($self->{IMG}, $IO) ) {
1225 $self->{ERRSTR}='unable to write png image';
1226 return undef;
1227 }
1228 $self->{DEBUG} && print "writing a png file\n";
9d540150 1229 } elsif ( $input{'type'} eq 'jpeg' ) {
cf692b64 1230 if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
f873cb01 1231 $self->{ERRSTR} = $self->_error_as_msg();
cf692b64
TC
1232 return undef;
1233 }
1234 $self->{DEBUG} && print "writing a jpeg file\n";
9d540150 1235 } elsif ( $input{'type'} eq 'bmp' ) {
705fd961
TC
1236 if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
1237 $self->{ERRSTR}='unable to write bmp image';
1238 return undef;
1239 }
1240 $self->{DEBUG} && print "writing a bmp file\n";
9d540150 1241 } elsif ( $input{'type'} eq 'tga' ) {
febba01f
AMH
1242
1243 if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
1ec86afa 1244 $self->{ERRSTR}=$self->_error_as_msg();
1ec86afa
AMH
1245 return undef;
1246 }
1247 $self->{DEBUG} && print "writing a tga file\n";
10461f9a
TC
1248 } elsif ( $input{'type'} eq 'gif' ) {
1249 # compatibility with the old interfaces
1250 if ($input{gifquant} eq 'lm') {
1251 $input{make_colors} = 'addi';
1252 $input{translate} = 'perturb';
1253 $input{perturb} = $input{lmdither};
1254 } elsif ($input{gifquant} eq 'gen') {
1255 # just pass options through
1256 } else {
1257 $input{make_colors} = 'webmap'; # ignored
1258 $input{translate} = 'giflib';
1259 }
1260 $rc = i_writegif_wiol($IO, \%input, $self->{IMG});
02d1d628
AMH
1261 }
1262
930c67c8
AMH
1263 if (exists $input{'data'}) {
1264 my $data = io_slurp($IO);
1265 if (!$data) {
1266 $self->{ERRSTR}='Could not slurp from buffer';
1267 return undef;
1268 }
1269 ${$input{data}} = $data;
1270 }
02d1d628 1271 return $self;
02d1d628 1272 }
10461f9a 1273
02d1d628
AMH
1274 return $self;
1275}
1276
1277sub write_multi {
1278 my ($class, $opts, @images) = @_;
1279
10461f9a
TC
1280 if (!$opts->{'type'} && $opts->{'file'}) {
1281 $opts->{'type'} = $FORMATGUESS->($opts->{'file'});
1282 }
1283 unless ($opts->{'type'}) {
1284 $class->_set_error('type parameter missing and not possible to guess from extension');
1285 return;
1286 }
1287 # translate to ImgRaw
1288 if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
1289 $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
1290 return 0;
1291 }
1292 my @work = map $_->{IMG}, @images;
1293 my ($IO, $file) = $class->_get_writer_io($opts, $opts->{'type'})
1294 or return undef;
9d540150 1295 if ($opts->{'type'} eq 'gif') {
ed88b092
TC
1296 my $gif_delays = $opts->{gif_delays};
1297 local $opts->{gif_delays} = $gif_delays;
10461f9a 1298 if ($opts->{gif_delays} && !ref $opts->{gif_delays}) {
ed88b092
TC
1299 # assume the caller wants the same delay for each frame
1300 $opts->{gif_delays} = [ ($gif_delays) x @images ];
1301 }
10461f9a
TC
1302 my $res = i_writegif_wiol($IO, $opts, @work);
1303 $res or $class->_set_error($class->_error_as_msg());
1304 return $res;
1305 }
1306 elsif ($opts->{'type'} eq 'tiff') {
1307 my $res;
1308 $opts->{fax_fine} = 1 unless exists $opts->{fax_fine};
1309 if ($opts->{'class'} && $opts->{'class'} eq 'fax') {
1310 $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
02d1d628
AMH
1311 }
1312 else {
10461f9a 1313 $res = i_writetiff_multi_wiol($IO, @work);
02d1d628 1314 }
10461f9a
TC
1315 $res or $class->_set_error($class->_error_as_msg());
1316 return $res;
02d1d628
AMH
1317 }
1318 else {
9d540150 1319 $ERRSTR = "Sorry, write_multi doesn't support $opts->{'type'} yet";
02d1d628
AMH
1320 return 0;
1321 }
1322}
1323
faa9b3e7
TC
1324# read multiple images from a file
1325sub read_multi {
1326 my ($class, %opts) = @_;
1327
9d540150 1328 if ($opts{file} && !exists $opts{'type'}) {
faa9b3e7
TC
1329 # guess the type
1330 my $type = $FORMATGUESS->($opts{file});
9d540150 1331 $opts{'type'} = $type;
faa9b3e7 1332 }
9d540150 1333 unless ($opts{'type'}) {
faa9b3e7
TC
1334 $ERRSTR = "No type parameter supplied and it couldn't be guessed";
1335 return;
1336 }
faa9b3e7 1337
10461f9a
TC
1338 my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
1339 or return;
9d540150 1340 if ($opts{'type'} eq 'gif') {
faa9b3e7 1341 my @imgs;
10461f9a
TC
1342 @imgs = i_readgif_multi_wiol($IO);
1343 if (@imgs) {
1344 return map {
1345 bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
1346 } @imgs;
faa9b3e7
TC
1347 }
1348 else {
10461f9a
TC
1349 $ERRSTR = _error_as_msg();
1350 return;
faa9b3e7 1351 }
10461f9a
TC
1352 }
1353 elsif ($opts{'type'} eq 'tiff') {
1354 my @imgs = i_readtiff_multi_wiol($IO, -1);
faa9b3e7
TC
1355 if (@imgs) {
1356 return map {
1357 bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
1358 } @imgs;
1359 }
1360 else {
1361 $ERRSTR = _error_as_msg();
1362 return;
1363 }
1364 }
1365
9d540150 1366 $ERRSTR = "Cannot read multiple images from $opts{'type'} files";
faa9b3e7
TC
1367 return;
1368}
1369
02d1d628
AMH
1370# Destroy an Imager object
1371
1372sub DESTROY {
1373 my $self=shift;
1374 # delete $instances{$self};
1375 if (defined($self->{IMG})) {
faa9b3e7
TC
1376 # the following is now handled by the XS DESTROY method for
1377 # Imager::ImgRaw object
1378 # Re-enabling this will break virtual images
1379 # tested for in t/t020masked.t
1380 # i_img_destroy($self->{IMG});
02d1d628
AMH
1381 undef($self->{IMG});
1382 } else {
1383# print "Destroy Called on an empty image!\n"; # why did I put this here??
1384 }
1385}
1386
1387# Perform an inplace filter of an image
1388# that is the image will be overwritten with the data
1389
1390sub filter {
1391 my $self=shift;
1392 my %input=@_;
1393 my %hsh;
1394 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1395
9d540150 1396 if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; }
02d1d628 1397
9d540150 1398 if ( (grep { $_ eq $input{'type'} } keys %filters) != 1) {
02d1d628
AMH
1399 $self->{ERRSTR}='type parameter not matching any filter'; return undef;
1400 }
1401
9d540150
TC
1402 if ($filters{$input{'type'}}{names}) {
1403 my $names = $filters{$input{'type'}}{names};
6607600c
TC
1404 for my $name (keys %$names) {
1405 if (defined $input{$name} && exists $names->{$name}{$input{$name}}) {
1406 $input{$name} = $names->{$name}{$input{$name}};
1407 }
1408 }
1409 }
9d540150
TC
1410 if (defined($filters{$input{'type'}}{defaults})) {
1411 %hsh=('image',$self->{IMG},%{$filters{$input{'type'}}{defaults}},%input);
02d1d628
AMH
1412 } else {
1413 %hsh=('image',$self->{IMG},%input);
1414 }
1415
9d540150 1416 my @cs=@{$filters{$input{'type'}}{callseq}};
02d1d628
AMH
1417
1418 for(@cs) {
1419 if (!defined($hsh{$_})) {
9d540150 1420 $self->{ERRSTR}="missing parameter '$_' for filter ".$input{'type'}; return undef;
02d1d628
AMH
1421 }
1422 }
1423
9d540150 1424 &{$filters{$input{'type'}}{callsub}}(%hsh);
02d1d628
AMH
1425
1426 my @b=keys %hsh;
1427
1428 $self->{DEBUG} && print "callseq is: @cs\n";
1429 $self->{DEBUG} && print "matching callseq is: @b\n";
1430
1431 return $self;
1432}
1433
1434# Scale an image to requested size and return the scaled version
1435
1436sub scale {
1437 my $self=shift;
9d540150 1438 my %opts=(scalefactor=>0.5,'type'=>'max',qtype=>'normal',@_);
02d1d628
AMH
1439 my $img = Imager->new();
1440 my $tmp = Imager->new();
1441
1442 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1443
9d540150 1444 if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
02d1d628 1445 my ($xpix,$ypix)=( $opts{xpixels}/$self->getwidth() , $opts{ypixels}/$self->getheight() );
9d540150
TC
1446 if ($opts{'type'} eq 'min') { $opts{scalefactor}=min($xpix,$ypix); }
1447 if ($opts{'type'} eq 'max') { $opts{scalefactor}=max($xpix,$ypix); }
02d1d628
AMH
1448 } elsif ($opts{xpixels}) { $opts{scalefactor}=$opts{xpixels}/$self->getwidth(); }
1449 elsif ($opts{ypixels}) { $opts{scalefactor}=$opts{ypixels}/$self->getheight(); }
1450
1451 if ($opts{qtype} eq 'normal') {
1452 $tmp->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
1453 if ( !defined($tmp->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
1454 $img->{IMG}=i_scaleaxis($tmp->{IMG},$opts{scalefactor},1);
1455 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
1456 return $img;
1457 }
1458 if ($opts{'qtype'} eq 'preview') {
1459 $img->{IMG}=i_scale_nn($self->{IMG},$opts{'scalefactor'},$opts{'scalefactor'});
1460 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
1461 return $img;
1462 }
1463 $self->{ERRSTR}='scale: invalid value for qtype'; return undef;
1464}
1465
1466# Scales only along the X axis
1467
1468sub scaleX {
1469 my $self=shift;
1470 my %opts=(scalefactor=>0.5,@_);
1471
1472 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1473
1474 my $img = Imager->new();
1475
1476 if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getwidth(); }
1477
1478 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1479 $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
1480
1481 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
1482 return $img;
1483}
1484
1485# Scales only along the Y axis
1486
1487sub scaleY {
1488 my $self=shift;
1489 my %opts=(scalefactor=>0.5,@_);
1490
1491 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1492
1493 my $img = Imager->new();
1494
1495 if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getheight(); }
1496
1497 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1498 $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},1);
1499
1500 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
1501 return $img;
1502}
1503
1504
1505# Transform returns a spatial transformation of the input image
1506# this moves pixels to a new location in the returned image.
1507# NOTE - should make a utility function to check transforms for
1508# stack overruns
1509
1510sub transform {
1511 my $self=shift;
1512 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1513 my %opts=@_;
1514 my (@op,@ropx,@ropy,$iop,$or,@parm,$expr,@xt,@yt,@pt,$numre);
1515
1516# print Dumper(\%opts);
1517# xopcopdes
1518
1519 if ( $opts{'xexpr'} and $opts{'yexpr'} ) {
1520 if (!$I2P) {
1521 eval ("use Affix::Infix2Postfix;");
1522 print $@;
1523 if ( $@ ) {
1524 $self->{ERRSTR}='transform: expr given and Affix::Infix2Postfix is not avaliable.';
1525 return undef;
1526 }
1527 $I2P=Affix::Infix2Postfix->new('ops'=>[{op=>'+',trans=>'Add'},
1528 {op=>'-',trans=>'Sub'},
1529 {op=>'*',trans=>'Mult'},
1530 {op=>'/',trans=>'Div'},
9d540150 1531 {op=>'-','type'=>'unary',trans=>'u-'},
02d1d628 1532 {op=>'**'},
9d540150 1533 {op=>'func','type'=>'unary'}],
02d1d628
AMH
1534 'grouping'=>[qw( \( \) )],
1535 'func'=>[qw( sin cos )],
1536 'vars'=>[qw( x y )]
1537 );
1538 }
1539
1540 @xt=$I2P->translate($opts{'xexpr'});
1541 @yt=$I2P->translate($opts{'yexpr'});
1542
1543 $numre=$I2P->{'numre'};
1544 @pt=(0,0);
1545
1546 for(@xt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'xopcodes'}},'Parm',$#pt); } else { push(@{$opts{'xopcodes'}},$_); } }
1547 for(@yt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'yopcodes'}},'Parm',$#pt); } else { push(@{$opts{'yopcodes'}},$_); } }
1548 @{$opts{'parm'}}=@pt;
1549 }
1550
1551# print Dumper(\%opts);
1552
1553 if ( !exists $opts{'xopcodes'} or @{$opts{'xopcodes'}}==0) {
1554 $self->{ERRSTR}='transform: no xopcodes given.';
1555 return undef;
1556 }
1557
1558 @op=@{$opts{'xopcodes'}};
1559 for $iop (@op) {
1560 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
1561 $self->{ERRSTR}="transform: illegal opcode '$_'.";
1562 return undef;
1563 }
1564 push(@ropx,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
1565 }
1566
1567
1568# yopcopdes
1569
1570 if ( !exists $opts{'yopcodes'} or @{$opts{'yopcodes'}}==0) {
1571 $self->{ERRSTR}='transform: no yopcodes given.';
1572 return undef;
1573 }
1574
1575 @op=@{$opts{'yopcodes'}};
1576 for $iop (@op) {
1577 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
1578 $self->{ERRSTR}="transform: illegal opcode '$_'.";
1579 return undef;
1580 }
1581 push(@ropy,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
1582 }
1583
1584#parameters
1585
1586 if ( !exists $opts{'parm'}) {
1587 $self->{ERRSTR}='transform: no parameter arg given.';
1588 return undef;
1589 }
1590
1591# print Dumper(\@ropx);
1592# print Dumper(\@ropy);
1593# print Dumper(\@ropy);
1594
1595 my $img = Imager->new();
1596 $img->{IMG}=i_transform($self->{IMG},\@ropx,\@ropy,$opts{'parm'});
1597 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='transform: failed'; return undef; }
1598 return $img;
1599}
1600
1601
bf94b653
TC
1602sub transform2 {
1603 my ($opts, @imgs) = @_;
1604
1605 require "Imager/Expr.pm";
1606
1607 $opts->{variables} = [ qw(x y) ];
1608 my ($width, $height) = @{$opts}{qw(width height)};
1609 if (@imgs) {
1610 $width ||= $imgs[0]->getwidth();
1611 $height ||= $imgs[0]->getheight();
1612 my $img_num = 1;
1613 for my $img (@imgs) {
1614 $opts->{constants}{"w$img_num"} = $img->getwidth();
1615 $opts->{constants}{"h$img_num"} = $img->getheight();
1616 $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
1617 $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
1618 ++$img_num;
02d1d628 1619 }
02d1d628 1620 }
bf94b653
TC
1621 if ($width) {
1622 $opts->{constants}{w} = $width;
1623 $opts->{constants}{cx} = $width/2;
1624 }
1625 else {
1626 $Imager::ERRSTR = "No width supplied";
1627 return;
1628 }
1629 if ($height) {
1630 $opts->{constants}{h} = $height;
1631 $opts->{constants}{cy} = $height/2;
1632 }
1633 else {
1634 $Imager::ERRSTR = "No height supplied";
1635 return;
1636 }
1637 my $code = Imager::Expr->new($opts);
1638 if (!$code) {
1639 $Imager::ERRSTR = Imager::Expr::error();
1640 return;
1641 }
9982a307 1642
bf94b653
TC
1643 my $img = Imager->new();
1644 $img->{IMG} = i_transform2($opts->{width}, $opts->{height}, $code->code(),
1645 $code->nregs(), $code->cregs(),
1646 [ map { $_->{IMG} } @imgs ]);
1647 if (!defined $img->{IMG}) {
1648 $Imager::ERRSTR = Imager->_error_as_msg();
1649 return;
1650 }
9982a307 1651
bf94b653 1652 return $img;
02d1d628
AMH
1653}
1654
02d1d628
AMH
1655sub rubthrough {
1656 my $self=shift;
1657 my %opts=(tx=>0,ty=>0,@_);
1658
1659 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1660 unless ($opts{src} && $opts{src}->{IMG}) { $self->{ERRSTR}='empty input image for source'; return undef; }
1661
faa9b3e7
TC
1662 unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx},$opts{ty})) {
1663 $self->{ERRSTR} = $self->_error_as_msg();
1664 return undef;
1665 }
02d1d628
AMH
1666 return $self;
1667}
1668
1669
142c26ff
AMH
1670sub flip {
1671 my $self = shift;
1672 my %opts = @_;
9191e525 1673 my %xlate = (h=>0, v=>1, hv=>2, vh=>2);
142c26ff
AMH
1674 my $dir;
1675 return () unless defined $opts{'dir'} and defined $xlate{$opts{'dir'}};
1676 $dir = $xlate{$opts{'dir'}};
1677 return $self if i_flipxy($self->{IMG}, $dir);
1678 return ();
1679}
1680
faa9b3e7
TC
1681sub rotate {
1682 my $self = shift;
1683 my %opts = @_;
1684 if (defined $opts{right}) {
1685 my $degrees = $opts{right};
1686 if ($degrees < 0) {
1687 $degrees += 360 * int(((-$degrees)+360)/360);
1688 }
1689 $degrees = $degrees % 360;
1690 if ($degrees == 0) {
1691 return $self->copy();
1692 }
1693 elsif ($degrees == 90 || $degrees == 180 || $degrees == 270) {
1694 my $result = Imager->new();
1695 if ($result->{IMG} = i_rotate90($self->{IMG}, $degrees)) {
1696 return $result;
1697 }
1698 else {
1699 $self->{ERRSTR} = $self->_error_as_msg();
1700 return undef;
1701 }
1702 }
1703 else {
1704 $self->{ERRSTR} = "Parameter 'right' must be a multiple of 90 degrees";
1705 return undef;
1706 }
1707 }
1708 elsif (defined $opts{radians} || defined $opts{degrees}) {
1709 my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
1710
1711 my $result = Imager->new;
1712 if ($result->{IMG} = i_rotate_exact($self->{IMG}, $amount)) {
1713 return $result;
1714 }
1715 else {
1716 $self->{ERRSTR} = $self->_error_as_msg();
1717 return undef;
1718 }
1719 }
1720 else {
1721 $self->{ERRSTR} = "Only the 'right' parameter is available";
1722 return undef;
1723 }
1724}
1725
1726sub matrix_transform {
1727 my $self = shift;
1728 my %opts = @_;
1729
1730 if ($opts{matrix}) {
1731 my $xsize = $opts{xsize} || $self->getwidth;
1732 my $ysize = $opts{ysize} || $self->getheight;
142c26ff 1733
faa9b3e7
TC
1734 my $result = Imager->new;
1735 $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
1736 $opts{matrix})
1737 or return undef;
1738
1739 return $result;
1740 }
1741 else {
1742 $self->{ERRSTR} = "matrix parameter required";
1743 return undef;
1744 }
1745}
1746
1747# blame Leolo :)
1748*yatf = \&matrix_transform;
02d1d628
AMH
1749
1750# These two are supported for legacy code only
1751
1752sub i_color_new {
faa9b3e7 1753 return Imager::Color->new(@_);
02d1d628
AMH
1754}
1755
1756sub i_color_set {
faa9b3e7 1757 return Imager::Color::set(@_);
02d1d628
AMH
1758}
1759
02d1d628 1760# Draws a box between the specified corner points.
02d1d628
AMH
1761sub box {
1762 my $self=shift;
1763 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1764 my $dflcl=i_color_new(255,255,255,255);
1765 my %opts=(color=>$dflcl,xmin=>0,ymin=>0,xmax=>$self->getwidth()-1,ymax=>$self->getheight()-1,@_);
1766
1767 if (exists $opts{'box'}) {
1768 $opts{'xmin'} = min($opts{'box'}->[0],$opts{'box'}->[2]);
1769 $opts{'xmax'} = max($opts{'box'}->[0],$opts{'box'}->[2]);
1770 $opts{'ymin'} = min($opts{'box'}->[1],$opts{'box'}->[3]);
1771 $opts{'ymax'} = max($opts{'box'}->[1],$opts{'box'}->[3]);
1772 }
1773
f1ac5027 1774 if ($opts{filled}) {
3a9a4241
TC
1775 my $color = _color($opts{'color'});
1776 unless ($color) {
1777 $self->{ERRSTR} = $Imager::ERRSTR;
1778 return;
1779 }
f1ac5027 1780 i_box_filled($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},
3a9a4241 1781 $opts{ymax}, $color);
f1ac5027
TC
1782 }
1783 elsif ($opts{fill}) {
1784 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
1785 # assume it's a hash ref
1786 require 'Imager/Fill.pm';
141a6114
TC
1787 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
1788 $self->{ERRSTR} = $Imager::ERRSTR;
1789 return undef;
1790 }
f1ac5027
TC
1791 }
1792 i_box_cfill($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},
1793 $opts{ymax},$opts{fill}{fill});
1794 }
cdd23610 1795 else {
3a9a4241
TC
1796 my $color = _color($opts{'color'});
1797 unless ($color) {
cdd23610
AMH
1798 $self->{ERRSTR} = $Imager::ERRSTR;
1799 return;
3a9a4241
TC
1800 }
1801 i_box($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},
1802 $color);
f1ac5027 1803 }
02d1d628
AMH
1804 return $self;
1805}
1806
1807# Draws an arc - this routine SUCKS and is buggy - it sometimes doesn't work when the arc is a convex polygon
1808
1809sub arc {
1810 my $self=shift;
1811 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1812 my $dflcl=i_color_new(255,255,255,255);
1813 my %opts=(color=>$dflcl,
1814 'r'=>min($self->getwidth(),$self->getheight())/3,
1815 'x'=>$self->getwidth()/2,
1816 'y'=>$self->getheight()/2,
1817 'd1'=>0, 'd2'=>361, @_);
f1ac5027
TC
1818 if ($opts{fill}) {
1819 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
1820 # assume it's a hash ref
1821 require 'Imager/Fill.pm';
569795e8
TC
1822 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
1823 $self->{ERRSTR} = $Imager::ERRSTR;
1824 return;
1825 }
f1ac5027
TC
1826 }
1827 i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
1828 $opts{'d2'}, $opts{fill}{fill});
1829 }
1830 else {
3a9a4241
TC
1831 my $color = _color($opts{'color'});
1832 unless ($color) {
1833 $self->{ERRSTR} = $Imager::ERRSTR;
1834 return;
1835 }
0d321238
TC
1836 if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
1837 i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
3a9a4241 1838 $color);
0d321238
TC
1839 }
1840 else {
3a9a4241
TC
1841 if ($opts{'d1'} <= $opts{'d2'}) {
1842 i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
1843 $opts{'d1'}, $opts{'d2'}, $color);
1844 }
1845 else {
1846 i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
1847 $opts{'d1'}, 361, $color);
1848 i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
1849 0, $opts{'d2'}, $color);
1850 }
0d321238 1851 }
f1ac5027
TC
1852 }
1853
02d1d628
AMH
1854 return $self;
1855}
1856
1857# Draws a line from one point to (but not including) the destination point
1858
1859sub line {
1860 my $self=shift;
1861 my $dflcl=i_color_new(0,0,0,0);
1862 my %opts=(color=>$dflcl,@_);
1863 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1864
1865 unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; }
1866 unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; }
1867
3a9a4241
TC
1868 my $color = _color($opts{'color'});
1869 unless ($color) {
1870 $self->{ERRSTR} = $Imager::ERRSTR;
1871 return;
1872 }
1873 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628 1874 if ($opts{antialias}) {
3a9a4241
TC
1875 i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
1876 $color);
02d1d628 1877 } else {
3a9a4241
TC
1878 i_draw($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
1879 $color);
02d1d628
AMH
1880 }
1881 return $self;
1882}
1883
1884# Draws a line between an ordered set of points - It more or less just transforms this
1885# into a list of lines.
1886
1887sub polyline {
1888 my $self=shift;
1889 my ($pt,$ls,@points);
1890 my $dflcl=i_color_new(0,0,0,0);
1891 my %opts=(color=>$dflcl,@_);
1892
1893 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1894
1895 if (exists($opts{points})) { @points=@{$opts{points}}; }
1896 if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) {
1897 @points=map { [ $opts{'x'}->[$_],$opts{'y'}->[$_] ] } (0..(scalar @{$opts{'x'}}-1));
1898 }
1899
1900# print Dumper(\@points);
1901
3a9a4241
TC
1902 my $color = _color($opts{'color'});
1903 unless ($color) {
1904 $self->{ERRSTR} = $Imager::ERRSTR;
1905 return;
1906 }
1907 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628
AMH
1908 if ($opts{antialias}) {
1909 for $pt(@points) {
3a9a4241
TC
1910 if (defined($ls)) {
1911 i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color);
1912 }
02d1d628
AMH
1913 $ls=$pt;
1914 }
1915 } else {
1916 for $pt(@points) {
3a9a4241
TC
1917 if (defined($ls)) {
1918 i_draw($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color);
1919 }
02d1d628
AMH
1920 $ls=$pt;
1921 }
1922 }
1923 return $self;
1924}
1925
d0e7bfee
AMH
1926sub polygon {
1927 my $self = shift;
1928 my ($pt,$ls,@points);
1929 my $dflcl = i_color_new(0,0,0,0);
1930 my %opts = (color=>$dflcl, @_);
1931
1932 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1933
1934 if (exists($opts{points})) {
1935 $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ];
1936 $opts{'y'} = [ map { $_->[1] } @{$opts{points}} ];
1937 }
1938
1939 if (!exists $opts{'x'} or !exists $opts{'y'}) {
1940 $self->{ERRSTR} = 'no points array, or x and y arrays.'; return undef;
1941 }
1942
43c5dacb
TC
1943 if ($opts{'fill'}) {
1944 unless (UNIVERSAL::isa($opts{'fill'}, 'Imager::Fill')) {
1945 # assume it's a hash ref
1946 require 'Imager/Fill.pm';
1947 unless ($opts{'fill'} = Imager::Fill->new(%{$opts{'fill'}})) {
1948 $self->{ERRSTR} = $Imager::ERRSTR;
1949 return undef;
1950 }
1951 }
1952 i_poly_aa_cfill($self->{IMG}, $opts{'x'}, $opts{'y'},
1953 $opts{'fill'}{'fill'});
1954 }
1955 else {
3a9a4241
TC
1956 my $color = _color($opts{'color'});
1957 unless ($color) {
1958 $self->{ERRSTR} = $Imager::ERRSTR;
1959 return;
1960 }
1961 i_poly_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
43c5dacb
TC
1962 }
1963
d0e7bfee
AMH
1964 return $self;
1965}
1966
1967
1968# this the multipoint bezier curve
02d1d628
AMH
1969# this is here more for testing that actual usage since
1970# this is not a good algorithm. Usually the curve would be
1971# broken into smaller segments and each done individually.
1972
1973sub polybezier {
1974 my $self=shift;
1975 my ($pt,$ls,@points);
1976 my $dflcl=i_color_new(0,0,0,0);
1977 my %opts=(color=>$dflcl,@_);
1978
1979 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1980
1981 if (exists $opts{points}) {
1982 $opts{'x'}=map { $_->[0]; } @{$opts{'points'}};
1983 $opts{'y'}=map { $_->[1]; } @{$opts{'points'}};
1984 }
1985
1986 unless ( @{$opts{'x'}} and @{$opts{'x'}} == @{$opts{'y'}} ) {
1987 $self->{ERRSTR}='Missing or invalid points.';
1988 return;
1989 }
1990
3a9a4241
TC
1991 my $color = _color($opts{'color'});
1992 unless ($color) {
1993 $self->{ERRSTR} = $Imager::ERRSTR;
1994 return;
1995 }
1996 i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$color);
02d1d628
AMH
1997 return $self;
1998}
1999
cc6483e0
TC
2000sub flood_fill {
2001 my $self = shift;
2002 my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
2003
9d540150 2004 unless (exists $opts{'x'} && exists $opts{'y'}) {
cc6483e0
TC
2005 $self->{ERRSTR} = "missing seed x and y parameters";
2006 return undef;
2007 }
07d70837 2008
cc6483e0
TC
2009 if ($opts{fill}) {
2010 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2011 # assume it's a hash ref
2012 require 'Imager/Fill.pm';
569795e8
TC
2013 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2014 $self->{ERRSTR} = $Imager::ERRSTR;
2015 return;
2016 }
cc6483e0 2017 }
9d540150 2018 i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
cc6483e0
TC
2019 }
2020 else {
3a9a4241
TC
2021 my $color = _color($opts{'color'});
2022 unless ($color) {
2023 $self->{ERRSTR} = $Imager::ERRSTR;
2024 return;
2025 }
2026 i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
cc6483e0
TC
2027 }
2028
2029 $self;
2030}
2031
591b5954
TC
2032sub setpixel {
2033 my $self = shift;
2034
2035 my %opts = ( color=>$self->{fg} || NC(255, 255, 255), @_);
2036
2037 unless (exists $opts{'x'} && exists $opts{'y'}) {
2038 $self->{ERRSTR} = 'missing x and y parameters';
2039 return undef;
2040 }
2041
2042 my $x = $opts{'x'};
2043 my $y = $opts{'y'};
2044 my $color = _color($opts{color})
2045 or return undef;
2046 if (ref $x && ref $y) {
2047 unless (@$x == @$y) {
2048 $self->{ERRSTR} = 'length of x and y mistmatch';
2049 return undef;
2050 }
2051 if ($color->isa('Imager::Color')) {
2052 for my $i (0..$#{$opts{'x'}}) {
2053 i_ppix($self->{IMG}, $x->[$i], $y->[$i], $color);
2054 }
2055 }
2056 else {
2057 for my $i (0..$#{$opts{'x'}}) {
2058 i_ppixf($self->{IMG}, $x->[$i], $y->[$i], $color);
2059 }
2060 }
2061 }
2062 else {
2063 if ($color->isa('Imager::Color')) {
2064 i_ppix($self->{IMG}, $x, $y, $color);
2065 }
2066 else {
2067 i_ppixf($self->{IMG}, $x, $y, $color);
2068 }
2069 }
2070
2071 $self;
2072}
2073
2074sub getpixel {
2075 my $self = shift;
2076
2077 my %opts = ( type=>'8bit', @_);
2078
2079 unless (exists $opts{'x'} && exists $opts{'y'}) {
2080 $self->{ERRSTR} = 'missing x and y parameters';
2081 return undef;
2082 }
2083
2084 my $x = $opts{'x'};
2085 my $y = $opts{'y'};
2086 if (ref $x && ref $y) {
2087 unless (@$x == @$y) {
2088 $self->{ERRSTR} = 'length of x and y mismatch';
2089 return undef;
2090 }
2091 my @result;
2092 if ($opts{type} eq '8bit') {
2093 for my $i (0..$#{$opts{'x'}}) {
2094 push(@result, i_get_pixel($self->{IMG}, $x->[$i], $y->[$i]));
2095 }
2096 }
2097 else {
2098 for my $i (0..$#{$opts{'x'}}) {
2099 push(@result, i_gpixf($self->{IMG}, $x->[$i], $y->[$i]));
2100 }
2101 }
2102 return wantarray ? @result : \@result;
2103 }
2104 else {
2105 if ($opts{type} eq '8bit') {
2106 return i_get_pixel($self->{IMG}, $x, $y);
2107 }
2108 else {
2109 return i_gpixf($self->{IMG}, $x, $y);
2110 }
2111 }
2112
2113 $self;
2114}
2115
f5991c03
TC
2116# make an identity matrix of the given size
2117sub _identity {
2118 my ($size) = @_;
2119
2120 my $matrix = [ map { [ (0) x $size ] } 1..$size ];
2121 for my $c (0 .. ($size-1)) {
2122 $matrix->[$c][$c] = 1;
2123 }
2124 return $matrix;
2125}
2126
2127# general function to convert an image
2128sub convert {
2129 my ($self, %opts) = @_;
2130 my $matrix;
2131
2132 # the user can either specify a matrix or preset
2133 # the matrix overrides the preset
2134 if (!exists($opts{matrix})) {
2135 unless (exists($opts{preset})) {
2136 $self->{ERRSTR} = "convert() needs a matrix or preset";
2137 return;
2138 }
2139 else {
2140 if ($opts{preset} eq 'gray' || $opts{preset} eq 'grey') {
2141 # convert to greyscale, keeping the alpha channel if any
2142 if ($self->getchannels == 3) {
2143 $matrix = [ [ 0.222, 0.707, 0.071 ] ];
2144 }
2145 elsif ($self->getchannels == 4) {
2146 # preserve the alpha channel
2147 $matrix = [ [ 0.222, 0.707, 0.071, 0 ],
2148 [ 0, 0, 0, 1 ] ];
2149 }
2150 else {
2151 # an identity
2152 $matrix = _identity($self->getchannels);
2153 }
2154 }
2155 elsif ($opts{preset} eq 'noalpha') {
2156 # strip the alpha channel
2157 if ($self->getchannels == 2 or $self->getchannels == 4) {
2158 $matrix = _identity($self->getchannels);
2159 pop(@$matrix); # lose the alpha entry
2160 }
2161 else {
2162 $matrix = _identity($self->getchannels);
2163 }
2164 }
2165 elsif ($opts{preset} eq 'red' || $opts{preset} eq 'channel0') {
2166 # extract channel 0
2167 $matrix = [ [ 1 ] ];
2168 }
2169 elsif ($opts{preset} eq 'green' || $opts{preset} eq 'channel1') {
2170 $matrix = [ [ 0, 1 ] ];
2171 }
2172 elsif ($opts{preset} eq 'blue' || $opts{preset} eq 'channel2') {
2173 $matrix = [ [ 0, 0, 1 ] ];
2174 }
2175 elsif ($opts{preset} eq 'alpha') {
2176 if ($self->getchannels == 2 or $self->getchannels == 4) {
2177 $matrix = [ [ (0) x ($self->getchannels-1), 1 ] ];
2178 }
2179 else {
2180 # the alpha is just 1 <shrug>
2181 $matrix = [ [ (0) x $self->getchannels, 1 ] ];
2182 }
2183 }
2184 elsif ($opts{preset} eq 'rgb') {
2185 if ($self->getchannels == 1) {
2186 $matrix = [ [ 1 ], [ 1 ], [ 1 ] ];
2187 }
2188 elsif ($self->getchannels == 2) {
2189 # preserve the alpha channel
2190 $matrix = [ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ], [ 0, 1 ] ];
2191 }
2192 else {
2193 $matrix = _identity($self->getchannels);
2194 }
2195 }
2196 elsif ($opts{preset} eq 'addalpha') {
2197 if ($self->getchannels == 1) {
2198 $matrix = _identity(2);
2199 }
2200 elsif ($self->getchannels == 3) {
2201 $matrix = _identity(4);
2202 }
2203 else {
2204 $matrix = _identity($self->getchannels);
2205 }
2206 }
2207 else {
2208 $self->{ERRSTR} = "Unknown convert preset $opts{preset}";
2209 return undef;
2210 }
2211 }
2212 }
2213 else {
2214 $matrix = $opts{matrix};
2215 }
2216
2217 my $new = Imager->new();
2218 $new->{IMG} = i_img_new();
2219 unless (i_convert($new->{IMG}, $self->{IMG}, $matrix)) {
2220 # most likely a bad matrix
2221 $self->{ERRSTR} = _error_as_msg();
2222 return undef;
2223 }
2224 return $new;
2225}
40eba1ea
AMH
2226
2227
40eba1ea 2228# general function to map an image through lookup tables
9495ee93 2229
40eba1ea
AMH
2230sub map {
2231 my ($self, %opts) = @_;
9495ee93 2232 my @chlist = qw( red green blue alpha );
40eba1ea
AMH
2233
2234 if (!exists($opts{'maps'})) {
2235 # make maps from channel maps
2236 my $chnum;
2237 for $chnum (0..$#chlist) {
9495ee93
AMH
2238 if (exists $opts{$chlist[$chnum]}) {
2239 $opts{'maps'}[$chnum] = $opts{$chlist[$chnum]};
2240 } elsif (exists $opts{'all'}) {
2241 $opts{'maps'}[$chnum] = $opts{'all'};
2242 }
40eba1ea
AMH
2243 }
2244 }
2245 if ($opts{'maps'} and $self->{IMG}) {
2246 i_map($self->{IMG}, $opts{'maps'} );
2247 }
2248 return $self;
2249}
2250
02d1d628
AMH
2251# destructive border - image is shrunk by one pixel all around
2252
2253sub border {
2254 my ($self,%opts)=@_;
2255 my($tx,$ty)=($self->getwidth()-1,$self->getheight()-1);
2256 $self->polyline('x'=>[0,$tx,$tx,0,0],'y'=>[0,0,$ty,$ty,0],%opts);
2257}
2258
2259
2260# Get the width of an image
2261
2262sub getwidth {
2263 my $self = shift;
2264 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
2265 return (i_img_info($self->{IMG}))[0];
2266}
2267
2268# Get the height of an image
2269
2270sub getheight {
2271 my $self = shift;
2272 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
2273 return (i_img_info($self->{IMG}))[1];
2274}
2275
2276# Get number of channels in an image
2277
2278sub getchannels {
2279 my $self = shift;
2280 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
2281 return i_img_getchannels($self->{IMG});
2282}
2283
2284# Get channel mask
2285
2286sub getmask {
2287 my $self = shift;
2288 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
2289 return i_img_getmask($self->{IMG});
2290}
2291
2292# Set channel mask
2293
2294sub setmask {
2295 my $self = shift;
2296 my %opts = @_;
2297 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
2298 i_img_setmask( $self->{IMG} , $opts{mask} );
2299}
2300
2301# Get number of colors in an image
2302
2303sub getcolorcount {
2304 my $self=shift;
9d540150 2305 my %opts=('maxcolors'=>2**30,@_);
02d1d628
AMH
2306 if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
2307 my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
2308 return ($rc==-1? undef : $rc);
2309}
2310
2311# draw string to an image
2312
2313sub string {
2314 my $self = shift;
2315 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2316
2317 my %input=('x'=>0, 'y'=>0, @_);
2318 $input{string}||=$input{text};
2319
2320 unless(exists $input{string}) {
2321 $self->{ERRSTR}="missing required parameter 'string'";
2322 return;
2323 }
2324
2325 unless($input{font}) {
2326 $self->{ERRSTR}="missing required parameter 'font'";
2327 return;
2328 }
2329
faa9b3e7
TC
2330 unless ($input{font}->draw(image=>$self, %input)) {
2331 $self->{ERRSTR} = $self->_error_as_msg();
2332 return;
2333 }
02d1d628
AMH
2334
2335 return $self;
2336}
2337
02d1d628
AMH
2338# Shortcuts that can be exported
2339
2340sub newcolor { Imager::Color->new(@_); }
2341sub newfont { Imager::Font->new(@_); }
2342
2343*NC=*newcolour=*newcolor;
2344*NF=*newfont;
2345
2346*open=\&read;
2347*circle=\&arc;
2348
2349
2350#### Utility routines
2351
faa9b3e7
TC
2352sub errstr {
2353 ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
2354}
02d1d628 2355
10461f9a
TC
2356sub _set_error {
2357 my ($self, $msg) = @_;
2358
2359 if (ref $self) {
2360 $self->{ERRSTR} = $msg;
2361 }
2362 else {
2363 $ERRSTR = $msg;
2364 }
2365}
2366
02d1d628
AMH
2367# Default guess for the type of an image from extension
2368
2369sub def_guess_type {
2370 my $name=lc(shift);
2371 my $ext;
2372 $ext=($name =~ m/\.([^\.]+)$/)[0];
2373 return 'tiff' if ($ext =~ m/^tiff?$/);
2374 return 'jpeg' if ($ext =~ m/^jpe?g$/);
2375 return 'pnm' if ($ext =~ m/^p[pgb]m$/);
2376 return 'png' if ($ext eq "png");
705fd961 2377 return 'bmp' if ($ext eq "bmp" || $ext eq "dib");
1ec86afa 2378 return 'tga' if ($ext eq "tga");
737a830c 2379 return 'rgb' if ($ext eq "rgb");
02d1d628 2380 return 'gif' if ($ext eq "gif");
10461f9a 2381 return 'raw' if ($ext eq "raw");
02d1d628
AMH
2382 return ();
2383}
2384
2385# get the minimum of a list
2386
2387sub min {
2388 my $mx=shift;
2389 for(@_) { if ($_<$mx) { $mx=$_; }}
2390 return $mx;
2391}
2392
2393# get the maximum of a list
2394
2395sub max {
2396 my $mx=shift;
2397 for(@_) { if ($_>$mx) { $mx=$_; }}
2398 return $mx;
2399}
2400
2401# string stuff for iptc headers
2402
2403sub clean {
2404 my($str)=$_[0];
2405 $str = substr($str,3);
2406 $str =~ s/[\n\r]//g;
2407 $str =~ s/\s+/ /g;
2408 $str =~ s/^\s//;
2409 $str =~ s/\s$//;
2410 return $str;
2411}
2412
2413# A little hack to parse iptc headers.
2414
2415sub parseiptc {
2416 my $self=shift;
2417 my(@sar,$item,@ar);
2418 my($caption,$photogr,$headln,$credit);
2419
2420 my $str=$self->{IPTCRAW};
2421
2422 #print $str;
2423
2424 @ar=split(/8BIM/,$str);
2425
2426 my $i=0;
2427 foreach (@ar) {
2428 if (/^\004\004/) {
2429 @sar=split(/\034\002/);
2430 foreach $item (@sar) {
cdd23610 2431 if ($item =~ m/^x/) {
02d1d628
AMH
2432 $caption=&clean($item);
2433 $i++;
2434 }
cdd23610 2435 if ($item =~ m/^P/) {
02d1d628
AMH
2436 $photogr=&clean($item);
2437 $i++;
2438 }
cdd23610 2439 if ($item =~ m/^i/) {
02d1d628
AMH
2440 $headln=&clean($item);
2441 $i++;
2442 }
cdd23610 2443 if ($item =~ m/^n/) {
02d1d628
AMH
2444 $credit=&clean($item);
2445 $i++;
2446 }
2447 }
2448 }
2449 }
2450 return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
2451}
2452
02d1d628
AMH
2453# Autoload methods go after =cut, and are processed by the autosplit program.
2454
24551;
2456__END__
2457# Below is the stub of documentation for your module. You better edit it!
2458
2459=head1 NAME
2460
2461Imager - Perl extension for Generating 24 bit Images
2462
2463=head1 SYNOPSIS
2464
0e418f1e
AMH
2465 # Thumbnail example
2466
2467 #!/usr/bin/perl -w
2468 use strict;
10461f9a 2469 use Imager;
02d1d628 2470
0e418f1e
AMH
2471 die "Usage: thumbmake.pl filename\n" if !-f $ARGV[0];
2472 my $file = shift;
2473
2474 my $format;
2475
2476 my $img = Imager->new();
2477 $img->open(file=>$file) or die $img->errstr();
2478
2479 $file =~ s/\.[^.]*$//;
2480
2481 # Create smaller version
2482 my $thumb = $img->scale(scalefactor=>.3);
2483
2484 # Autostretch individual channels
2485 $thumb->filter(type=>'autolevels');
2486
2487 # try to save in one of these formats
2488 SAVE:
2489
2490 for $format ( qw( png gif jpg tiff ppm ) ) {
2491 # Check if given format is supported
2492 if ($Imager::formats{$format}) {
2493 $file.="_low.$format";
2494 print "Storing image as: $file\n";
2495 $thumb->write(file=>$file) or
2496 die $thumb->errstr;
2497 last SAVE;
2498 }
2499 }
2500
2501
2502 # Logo Generator Example
2503
2504
02d1d628
AMH
2505
2506=head1 DESCRIPTION
2507
0e418f1e
AMH
2508Imager is a module for creating and altering images. It can read and
2509write various image formats, draw primitive shapes like lines,and
2510polygons, blend multiple images together in various ways, scale, crop,
2511render text and more.
02d1d628 2512
5df0fac7
AMH
2513=head2 Overview of documentation
2514
2515=over
2516
2517=item Imager
2518
0e418f1e 2519This document - Synopsis Example, Table of Contents and Overview.
5df0fac7
AMH
2520
2521=item Imager::ImageTypes
2522
2523Direct type/virtual images, RGB(A)/paletted images, 8/16/double
f5fd108b
AMH
2524bits/channel, color maps, channel masks, image tags, color
2525quantization.
5df0fac7
AMH
2526
2527=item Imager::Files
2528
2529IO interaction, reading/writing images, format specific tags.
2530
2531=item Imager::Draw
2532
f5fd108b 2533Drawing Primitives, lines, boxes, circles, arcs, flood fill.
5df0fac7
AMH
2534
2535=item Imager::Color
2536
2537Color specification.
2538
daa45279 2539=item Imager::Fill
f5fd108b
AMH
2540
2541Fill pattern specification.
2542
5df0fac7
AMH
2543=item Imager::Font
2544
f5fd108b 2545General font rendering, bounding boxes and font metrics.
5df0fac7
AMH
2546
2547=item Imager::Transformations
2548
f5fd108b
AMH
2549Copying, scaling, cropping, flipping, blending, pasting, convert and
2550map.
5df0fac7
AMH
2551
2552=item Imager::Engines
2553
daa45279
AMH
2554Programmable transformations through C<transform()>, C<transform2()>
2555and C<matrix_transform()>.
5df0fac7
AMH
2556
2557=item Imager::Filters
2558
f5fd108b 2559Filters, sharpen, blur, noise, convolve etc. and filter plugins.
5df0fac7
AMH
2560
2561=item Imager::Expr
2562
2563Expressions for evaluation engine used by transform2().
2564
2565=item Imager::Matrix2d
2566
2567Helper class for affine transformations.
2568
2569=item Imager::Fountain
2570
2571Helper for making gradient profiles.
2572
2573=back
2574
2575
0e418f1e 2576
0e418f1e 2577=head2 Basic Overview
02d1d628
AMH
2578
2579An Image object is created with C<$img = Imager-E<gt>new()> Should
2580this fail for some reason an explanation can be found in
2581C<$Imager::ERRSTR> usually error messages are stored in
2582C<$img-E<gt>{ERRSTR}>, but since no object is created this is the only
2583way to give back errors. C<$Imager::ERRSTR> is also used to report
2584all errors not directly associated with an image object. Examples:
2585
2586 $img=Imager->new(); # This is an empty image (size is 0 by 0)
2587 $img->open(file=>'lena.png',type=>'png'); # initializes from file
2588
2589or if you want to create an empty image:
2590
2591 $img=Imager->new(xsize=>400,ysize=>300,channels=>4);
2592
0e418f1e
AMH
2593This example creates a completely black image of width 400 and height
2594300 and 4 channels.
2595
f64132d2 2596=head1 SUPPORT
0e418f1e 2597
f64132d2
TC
2598You can ask for help, report bugs or express your undying love for
2599Imager on the Imager-devel mailing list.
02d1d628 2600
f64132d2
TC
2601To subscribe send a message with C<subscribe> in the body to:
2602
2603 imager-devel+request@molar.is
2604
2605or use the form at:
2606
2607 http://www.molar.is/en/lists/imager-devel/
2608
2609where you can also find the mailing list archive.
10461f9a 2610
3ed96cd3
TC
2611If you're into IRC, you can typically find the developers in #Imager
2612on irc.rhizomatic.net. As with any IRC channel, the participants
2613could be occupied or asleep, so please be patient.
2614
02d1d628
AMH
2615=head1 BUGS
2616
0e418f1e 2617Bugs are listed individually for relevant pod pages.
02d1d628
AMH
2618
2619=head1 AUTHOR
2620
f64132d2
TC
2621Arnar M. Hrafnkelsson (addi@umich.edu) and Tony Cook
2622(tony@imager.perl.org) See the README for a complete list.
02d1d628 2623
9495ee93 2624=head1 SEE ALSO
02d1d628 2625
07d70837 2626perl(1), Imager::Color(3), Imager::Font(3), Imager::Matrix2d(3),
009db950
AMH
2627
2628Affix::Infix2Postfix(3), Parse::RecDescent(3)
faa9b3e7 2629http://www.eecs.umich.edu/~addi/perl/Imager/
02d1d628
AMH
2630
2631=cut