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