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