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