re-work META.yml validation and fix the detected errors
[imager.git] / Imager.pm
CommitLineData
02d1d628
AMH
1package Imager;
2
02d1d628 3use strict;
50c75381 4use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR %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 21 i_list_formats
dd55acc8 22
02d1d628
AMH
23 i_color_new
24 i_color_set
25 i_color_info
dd55acc8 26
02d1d628
AMH
27 i_img_empty
28 i_img_empty_ch
29 i_img_exorcise
30 i_img_destroy
31
32 i_img_info
33
34 i_img_setmask
35 i_img_getmask
36
aa833c97 37 i_line
02d1d628
AMH
38 i_line_aa
39 i_box
40 i_box_filled
41 i_arc
18063344 42 i_circle_aa
dd55acc8 43
02d1d628
AMH
44 i_bezier_multi
45 i_poly_aa
43c5dacb 46 i_poly_aa_cfill
02d1d628
AMH
47
48 i_copyto
49 i_rubthru
50 i_scaleaxis
51 i_scale_nn
52 i_haar
53 i_count_colors
dd55acc8 54
02d1d628
AMH
55 i_gaussian
56 i_conv
dd55acc8 57
f5991c03 58 i_convert
40eba1ea 59 i_map
dd55acc8 60
02d1d628
AMH
61 i_img_diff
62
02d1d628
AMH
63 i_tt_set_aa
64 i_tt_cp
65 i_tt_text
66 i_tt_bbox
67
02d1d628 68 i_readpnm_wiol
067d6bdc 69 i_writeppm_wiol
02d1d628 70
895dbd34
AMH
71 i_readraw_wiol
72 i_writeraw_wiol
02d1d628
AMH
73
74 i_contrast
75 i_hardinvert
76 i_noise
77 i_bumpmap
78 i_postlevels
79 i_mosaic
80 i_watermark
dd55acc8 81
02d1d628
AMH
82 malloc_state
83
84 list_formats
dd55acc8 85
02d1d628
AMH
86 i_gifquant
87
88 newfont
89 newcolor
90 newcolour
91 NC
92 NF
bd8052a6 93 NCF
02d1d628
AMH
94);
95
9982a307 96@EXPORT=qw(
02d1d628
AMH
97 init_log
98 i_list_formats
99 i_has_format
100 malloc_state
101 i_color_new
102
103 i_img_empty
104 i_img_empty_ch
105 );
106
107%EXPORT_TAGS=
108 (handy => [qw(
109 newfont
110 newcolor
111 NF
112 NC
bd8052a6 113 NCF
02d1d628
AMH
114 )],
115 all => [@EXPORT_OK],
116 default => [qw(
117 load_plugin
118 unload_plugin
119 )]);
120
53a6bbd4
TC
121# registered file readers
122my %readers;
123
2b405c9e
TC
124# registered file writers
125my %writers;
126
53a6bbd4
TC
127# modules we attempted to autoload
128my %attempted_to_load;
129
f245645a
TC
130# library keys that are image file formats
131my %file_formats = map { $_ => 1 } qw/tiff pnm gif png jpeg raw bmp tga/;
132
9b1ec2b8
TC
133# image pixel combine types
134my @combine_types =
135 qw/none normal multiply dissolve add subtract diff lighten darken
136 hue saturation value color/;
137my %combine_types;
138@combine_types{@combine_types} = 0 .. $#combine_types;
139$combine_types{mult} = $combine_types{multiply};
140$combine_types{'sub'} = $combine_types{subtract};
141$combine_types{sat} = $combine_types{saturation};
142
143# this will be used to store global defaults at some point
144my %defaults;
145
02d1d628
AMH
146BEGIN {
147 require Exporter;
b1e66a82
TC
148 if ($Exporter::VERSION < 5.57) {
149 @ISA = qw(Exporter);
150 }
101861e2 151 $VERSION = '0.83';
92bda632
TC
152 eval {
153 require XSLoader;
154 XSLoader::load(Imager => $VERSION);
155 1;
156 } or do {
157 require DynaLoader;
158 push @ISA, 'DynaLoader';
159 bootstrap Imager $VERSION;
160 }
02d1d628
AMH
161}
162
1d7e3124
TC
163my %formats_low;
164my %format_classes =
165 (
166 png => "Imager::File::PNG",
167 gif => "Imager::File::GIF",
168 tiff => "Imager::File::TIFF",
169 jpeg => "Imager::File::JPEG",
718b8c97 170 w32 => "Imager::Font::W32",
50c75381 171 ft2 => "Imager::Font::FT2",
a556912d 172 t1 => "Imager::Font::T1",
1d7e3124
TC
173 );
174
175tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
176
02d1d628 177BEGIN {
1d7e3124 178 for(i_list_formats()) { $formats_low{$_}++; }
02d1d628 179
02d1d628
AMH
180 %OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
181
182 $DEBUG=0;
183
6607600c
TC
184 # the members of the subhashes under %filters are:
185 # callseq - a list of the parameters to the underlying filter in the
186 # order they are passed
187 # callsub - a code ref that takes a named parameter list and calls the
188 # underlying filter
189 # defaults - a hash of default values
190 # names - defines names for value of given parameters so if the names
191 # field is foo=> { bar=>1 }, and the user supplies "bar" as the
192 # foo parameter, the filter will receive 1 for the foo
193 # parameter
02d1d628
AMH
194 $filters{contrast}={
195 callseq => ['image','intensity'],
196 callsub => sub { my %hsh=@_; i_contrast($hsh{image},$hsh{intensity}); }
197 };
198
199 $filters{noise} ={
200 callseq => ['image', 'amount', 'subtype'],
201 defaults => { amount=>3,subtype=>0 },
202 callsub => sub { my %hsh=@_; i_noise($hsh{image},$hsh{amount},$hsh{subtype}); }
203 };
204
205 $filters{hardinvert} ={
206 callseq => ['image'],
207 defaults => { },
208 callsub => sub { my %hsh=@_; i_hardinvert($hsh{image}); }
209 };
210
5558f899
TC
211 $filters{hardinvertall} =
212 {
213 callseq => ['image'],
214 defaults => { },
215 callsub => sub { my %hsh=@_; i_hardinvertall($hsh{image}); }
216 };
217
02d1d628
AMH
218 $filters{autolevels} ={
219 callseq => ['image','lsat','usat','skew'],
220 defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 },
221 callsub => sub { my %hsh=@_; i_autolevels($hsh{image},$hsh{lsat},$hsh{usat},$hsh{skew}); }
222 };
223
224 $filters{turbnoise} ={
225 callseq => ['image'],
226 defaults => { xo=>0.0,yo=>0.0,scale=>10.0 },
227 callsub => sub { my %hsh=@_; i_turbnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{scale}); }
228 };
229
230 $filters{radnoise} ={
231 callseq => ['image'],
232 defaults => { xo=>100,yo=>100,ascale=>17.0,rscale=>0.02 },
233 callsub => sub { my %hsh=@_; i_radnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{rscale},$hsh{ascale}); }
234 };
235
6a3cbaef
TC
236 $filters{conv} =
237 {
238 callseq => ['image', 'coef'],
239 defaults => { },
240 callsub =>
241 sub {
242 my %hsh=@_;
243 i_conv($hsh{image},$hsh{coef})
244 or die Imager->_error_as_msg() . "\n";
245 }
246 };
02d1d628 247
f0ddaffd
TC
248 $filters{gradgen} =
249 {
250 callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
251 defaults => { dist => 0 },
252 callsub =>
253 sub {
254 my %hsh=@_;
255 my @colors = @{$hsh{colors}};
256 $_ = _color($_)
257 for @colors;
258 i_gradgen($hsh{image}, $hsh{xo}, $hsh{yo}, \@colors, $hsh{dist});
259 }
260 };
02d1d628 261
e310e5f9
TC
262 $filters{nearest_color} =
263 {
264 callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
265 defaults => { },
266 callsub =>
267 sub {
268 my %hsh=@_;
269 # make sure the segments are specified with colors
270 my @colors;
271 for my $color (@{$hsh{colors}}) {
272 my $new_color = _color($color)
273 or die $Imager::ERRSTR."\n";
274 push @colors, $new_color;
275 }
276
277 i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, \@colors,
278 $hsh{dist})
279 or die Imager->_error_as_msg() . "\n";
280 },
281 };
faa9b3e7
TC
282 $filters{gaussian} = {
283 callseq => [ 'image', 'stddev' ],
284 defaults => { },
285 callsub => sub { my %hsh = @_; i_gaussian($hsh{image}, $hsh{stddev}); },
286 };
d08b8f85
TC
287 $filters{mosaic} =
288 {
289 callseq => [ qw(image size) ],
290 defaults => { size => 20 },
291 callsub => sub { my %hsh = @_; i_mosaic($hsh{image}, $hsh{size}) },
292 };
293 $filters{bumpmap} =
294 {
295 callseq => [ qw(image bump elevation lightx lighty st) ],
296 defaults => { elevation=>0, st=> 2 },
b2778574 297 callsub => sub {
d08b8f85
TC
298 my %hsh = @_;
299 i_bumpmap($hsh{image}, $hsh{bump}{IMG}, $hsh{elevation},
300 $hsh{lightx}, $hsh{lighty}, $hsh{st});
301 },
302 };
b2778574
AMH
303 $filters{bumpmap_complex} =
304 {
305 callseq => [ qw(image bump channel tx ty Lx Ly Lz cd cs n Ia Il Is) ],
306 defaults => {
307 channel => 0,
308 tx => 0,
309 ty => 0,
310 Lx => 0.2,
311 Ly => 0.4,
312 Lz => -1.0,
313 cd => 1.0,
314 cs => 40,
315 n => 1.3,
ffddd407
TC
316 Ia => [0,0,0],
317 Il => [255,255,255],
318 Is => [255,255,255],
b2778574
AMH
319 },
320 callsub => sub {
321 my %hsh = @_;
ffddd407
TC
322 for my $cname (qw/Ia Il Is/) {
323 my $old = $hsh{$cname};
324 my $new_color = _color($old)
325 or die $Imager::ERRSTR, "\n";
326 $hsh{$cname} = $new_color;
327 }
b2778574
AMH
328 i_bumpmap_complex($hsh{image}, $hsh{bump}{IMG}, $hsh{channel},
329 $hsh{tx}, $hsh{ty}, $hsh{Lx}, $hsh{Ly}, $hsh{Lz},
330 $hsh{cd}, $hsh{cs}, $hsh{n}, $hsh{Ia}, $hsh{Il},
331 $hsh{Is});
332 },
333 };
d08b8f85
TC
334 $filters{postlevels} =
335 {
336 callseq => [ qw(image levels) ],
337 defaults => { levels => 10 },
338 callsub => sub { my %hsh = @_; i_postlevels($hsh{image}, $hsh{levels}); },
339 };
340 $filters{watermark} =
341 {
342 callseq => [ qw(image wmark tx ty pixdiff) ],
343 defaults => { pixdiff=>10, tx=>0, ty=>0 },
344 callsub =>
345 sub {
346 my %hsh = @_;
347 i_watermark($hsh{image}, $hsh{wmark}{IMG}, $hsh{tx}, $hsh{ty},
348 $hsh{pixdiff});
349 },
350 };
6607600c
TC
351 $filters{fountain} =
352 {
353 callseq => [ qw(image xa ya xb yb ftype repeat combine super_sample ssample_param segments) ],
354 names => {
355 ftype => { linear => 0,
356 bilinear => 1,
357 radial => 2,
358 radial_square => 3,
359 revolution => 4,
360 conical => 5 },
361 repeat => { none => 0,
362 sawtooth => 1,
363 triangle => 2,
364 saw_both => 3,
365 tri_both => 4,
366 },
367 super_sample => {
368 none => 0,
369 grid => 1,
370 random => 2,
371 circle => 3,
372 },
efdc2568
TC
373 combine => {
374 none => 0,
375 normal => 1,
376 multiply => 2, mult => 2,
377 dissolve => 3,
378 add => 4,
9d540150 379 subtract => 5, 'sub' => 5,
efdc2568
TC
380 diff => 6,
381 lighten => 7,
382 darken => 8,
383 hue => 9,
384 sat => 10,
385 value => 11,
386 color => 12,
387 },
6607600c
TC
388 },
389 defaults => { ftype => 0, repeat => 0, combine => 0,
390 super_sample => 0, ssample_param => 4,
391 segments=>[
392 [ 0, 0.5, 1,
ffddd407
TC
393 [0,0,0],
394 [255, 255, 255],
6607600c
TC
395 0, 0,
396 ],
397 ],
398 },
399 callsub =>
400 sub {
401 my %hsh = @_;
109bec2d
TC
402
403 # make sure the segments are specified with colors
404 my @segments;
405 for my $segment (@{$hsh{segments}}) {
406 my @new_segment = @$segment;
407
408 $_ = _color($_) or die $Imager::ERRSTR."\n" for @new_segment[3,4];
409 push @segments, \@new_segment;
410 }
411
6607600c
TC
412 i_fountain($hsh{image}, $hsh{xa}, $hsh{ya}, $hsh{xb}, $hsh{yb},
413 $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
e310e5f9
TC
414 $hsh{ssample_param}, \@segments)
415 or die Imager->_error_as_msg() . "\n";
6607600c
TC
416 },
417 };
b6381851
TC
418 $filters{unsharpmask} =
419 {
420 callseq => [ qw(image stddev scale) ],
421 defaults => { stddev=>2.0, scale=>1.0 },
422 callsub =>
423 sub {
424 my %hsh = @_;
425 i_unsharp_mask($hsh{image}, $hsh{stddev}, $hsh{scale});
426 },
427 };
02d1d628
AMH
428
429 $FORMATGUESS=\&def_guess_type;
97c4effc
TC
430
431 $warn_obsolete = 1;
02d1d628
AMH
432}
433
434#
435# Non methods
436#
437
438# initlize Imager
439# NOTE: this might be moved to an import override later on
440
bd8052a6
TC
441sub import {
442 my $i = 1;
443 while ($i < @_) {
444 if ($_[$i] eq '-log-stderr') {
445 init_log(undef, 4);
446 splice(@_, $i, 1);
447 }
448 else {
449 ++$i;
450 }
451 }
452 goto &Exporter::import;
453}
02d1d628 454
f83bf98a 455sub init_log {
10ea52a3 456 Imager->open_log(log => $_[0], level => $_[1]);
f83bf98a
AMH
457}
458
459
02d1d628
AMH
460sub init {
461 my %parms=(loglevel=>1,@_);
462 if ($parms{'log'}) {
10ea52a3 463 Imager->open_log(log => $parms{log}, level => $parms{loglevel});
02d1d628 464 }
f83bf98a 465
97c4effc
TC
466 if (exists $parms{'warn_obsolete'}) {
467 $warn_obsolete = $parms{'warn_obsolete'};
468 }
02d1d628 469
4cb58f1b 470 if (exists $parms{'t1log'}) {
a556912d
TC
471 if ($formats{t1}) {
472 Imager::Font::T1::i_init_t1($parms{'t1log'});
473 }
4cb58f1b 474 }
02d1d628
AMH
475}
476
10ea52a3
TC
477{
478 my $is_logging = 0;
479
480 sub open_log {
481 my $class = shift;
482 my (%opts) = ( loglevel => 1, @_ );
483
484 $is_logging = i_init_log($opts{log}, $opts{loglevel});
485 unless ($is_logging) {
486 Imager->_set_error(Imager->_error_as_msg());
487 return;
488 }
489
490 Imager->log("Imager $VERSION starting\n", 1);
491
492 return $is_logging;
493 }
494
495 sub close_log {
496 i_init_log(undef, -1);
497 $is_logging = 0;
498 }
499
500 sub log {
501 my ($class, $message, $level) = @_;
502
503 defined $level or $level = 1;
504
505 i_log_entry($message, $level);
506 }
507
508 sub is_logging {
509 return $is_logging;
510 }
511}
512
02d1d628
AMH
513END {
514 if ($DEBUG) {
515 print "shutdown code\n";
516 # for(keys %instances) { $instances{$_}->DESTROY(); }
517 malloc_state(); # how do decide if this should be used? -- store something from the import
518 print "Imager exiting\n";
519 }
520}
521
522# Load a filter plugin
523
524sub load_plugin {
525 my ($filename)=@_;
526 my $i;
527 my ($DSO_handle,$str)=DSO_open($filename);
528 if (!defined($DSO_handle)) { $Imager::ERRSTR="Couldn't load plugin '$filename'\n"; return undef; }
529 my %funcs=DSO_funclist($DSO_handle);
530 if ($DEBUG) { print "loading module $filename\n"; $i=0; for(keys %funcs) { printf(" %2d: %s\n",$i++,$_); } }
531 $i=0;
532 for(keys %funcs) { if ($filters{$_}) { $ERRSTR="filter '$_' already exists\n"; DSO_close($DSO_handle); return undef; } }
533
534 $DSOs{$filename}=[$DSO_handle,\%funcs];
535
536 for(keys %funcs) {
537 my $evstr="\$filters{'".$_."'}={".$funcs{$_}.'};';
538 $DEBUG && print "eval string:\n",$evstr,"\n";
539 eval $evstr;
540 print $@ if $@;
541 }
542 return 1;
543}
544
545# Unload a plugin
546
547sub unload_plugin {
548 my ($filename)=@_;
549
550 if (!$DSOs{$filename}) { $ERRSTR="plugin '$filename' not loaded."; return undef; }
551 my ($DSO_handle,$funcref)=@{$DSOs{$filename}};
552 for(keys %{$funcref}) {
553 delete $filters{$_};
554 $DEBUG && print "unloading: $_\n";
555 }
556 my $rc=DSO_close($DSO_handle);
557 if (!defined($rc)) { $ERRSTR="unable to unload plugin '$filename'."; return undef; }
558 return 1;
559}
560
64606cc7
TC
561# take the results of i_error() and make a message out of it
562sub _error_as_msg {
563 return join(": ", map $_->[0], i_errors());
564}
565
3a9a4241
TC
566# this function tries to DWIM for color parameters
567# color objects are used as is
568# simple scalars are simply treated as single parameters to Imager::Color->new
569# hashrefs are treated as named argument lists to Imager::Color->new
570# arrayrefs are treated as list arguments to Imager::Color->new iff any
571# parameter is > 1
572# other arrayrefs are treated as list arguments to Imager::Color::Float
573
574sub _color {
575 my $arg = shift;
b6cfd214
TC
576 # perl 5.6.0 seems to do weird things to $arg if we don't make an
577 # explicitly stringified copy
578 # I vaguely remember a bug on this on p5p, but couldn't find it
579 # through bugs.perl.org (I had trouble getting it to find any bugs)
580 my $copy = $arg . "";
3a9a4241
TC
581 my $result;
582
583 if (ref $arg) {
584 if (UNIVERSAL::isa($arg, "Imager::Color")
585 || UNIVERSAL::isa($arg, "Imager::Color::Float")) {
586 $result = $arg;
587 }
588 else {
b6cfd214 589 if ($copy =~ /^HASH\(/) {
3a9a4241
TC
590 $result = Imager::Color->new(%$arg);
591 }
b6cfd214 592 elsif ($copy =~ /^ARRAY\(/) {
5daa8f70 593 $result = Imager::Color->new(@$arg);
3a9a4241
TC
594 }
595 else {
596 $Imager::ERRSTR = "Not a color";
597 }
598 }
599 }
600 else {
601 # assume Imager::Color::new knows how to handle it
602 $result = Imager::Color->new($arg);
603 }
604
605 return $result;
606}
607
9b1ec2b8
TC
608sub _combine {
609 my ($self, $combine, $default) = @_;
610
611 if (!defined $combine && ref $self) {
612 $combine = $self->{combine};
613 }
614 defined $combine or $combine = $defaults{combine};
615 defined $combine or $combine = $default;
616
617 if (exists $combine_types{$combine}) {
618 $combine = $combine_types{$combine};
619 }
620
621 return $combine;
622}
623
4cda4e76
TC
624sub _valid_image {
625 my ($self) = @_;
626
627 $self->{IMG} and return 1;
628
629 $self->_set_error('empty input image');
630
631 return;
632}
3a9a4241 633
500888da
TC
634# returns first defined parameter
635sub _first {
636 for (@_) {
637 return $_ if defined $_;
638 }
639 return undef;
640}
641
02d1d628
AMH
642#
643# Methods to be called on objects.
644#
645
646# Create a new Imager object takes very few parameters.
647# usually you call this method and then call open from
648# the resulting object
649
650sub new {
651 my $class = shift;
652 my $self ={};
653 my %hsh=@_;
654 bless $self,$class;
655 $self->{IMG}=undef; # Just to indicate what exists
656 $self->{ERRSTR}=undef; #
657 $self->{DEBUG}=$DEBUG;
3c252111
TC
658 $self->{DEBUG} and print "Initialized Imager\n";
659 if (defined $hsh{xsize} || defined $hsh{ysize}) {
1501d9b3
TC
660 unless ($self->img_set(%hsh)) {
661 $Imager::ERRSTR = $self->{ERRSTR};
662 return;
663 }
664 }
3c252111
TC
665 elsif (defined $hsh{file} ||
666 defined $hsh{fh} ||
667 defined $hsh{fd} ||
668 defined $hsh{callback} ||
75812841
TC
669 defined $hsh{readcb} ||
670 defined $hsh{data}) {
3c252111
TC
671 # allow $img = Imager->new(file => $filename)
672 my %extras;
673
674 # type is already used as a parameter to new(), rename it for the
675 # call to read()
676 if ($hsh{filetype}) {
677 $extras{type} = $hsh{filetype};
678 }
679 unless ($self->read(%hsh, %extras)) {
680 $Imager::ERRSTR = $self->{ERRSTR};
681 return;
682 }
683 }
684
02d1d628
AMH
685 return $self;
686}
687
02d1d628
AMH
688# Copy an entire image with no changes
689# - if an image has magic the copy of it will not be magical
690
691sub copy {
692 my $self = shift;
693 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
694
34b3f7e6
TC
695 unless (defined wantarray) {
696 my @caller = caller;
697 warn "copy() called in void context - copy() returns the copied image at $caller[1] line $caller[2]\n";
698 return;
699 }
700
02d1d628 701 my $newcopy=Imager->new();
92bda632 702 $newcopy->{IMG} = i_copy($self->{IMG});
02d1d628
AMH
703 return $newcopy;
704}
705
706# Paste a region
707
708sub paste {
709 my $self = shift;
92bda632
TC
710
711 unless ($self->{IMG}) {
712 $self->_set_error('empty input image');
713 return;
714 }
715 my %input=(left=>0, top=>0, src_minx => 0, src_miny => 0, @_);
716 my $src = $input{img} || $input{src};
717 unless($src) {
718 $self->_set_error("no source image");
02d1d628
AMH
719 return;
720 }
721 $input{left}=0 if $input{left} <= 0;
722 $input{top}=0 if $input{top} <= 0;
92bda632 723
02d1d628 724 my($r,$b)=i_img_info($src->{IMG});
92bda632
TC
725 my ($src_left, $src_top) = @input{qw/src_minx src_miny/};
726 my ($src_right, $src_bottom);
727 if ($input{src_coords}) {
728 ($src_left, $src_top, $src_right, $src_bottom) = @{$input{src_coords}}
729 }
730 else {
731 if (defined $input{src_maxx}) {
732 $src_right = $input{src_maxx};
733 }
734 elsif (defined $input{width}) {
735 if ($input{width} <= 0) {
736 $self->_set_error("paste: width must me positive");
737 return;
738 }
739 $src_right = $src_left + $input{width};
740 }
741 else {
742 $src_right = $r;
743 }
35029411 744 if (defined $input{src_maxy}) {
92bda632
TC
745 $src_bottom = $input{src_maxy};
746 }
747 elsif (defined $input{height}) {
748 if ($input{height} < 0) {
749 $self->_set_error("paste: height must be positive");
750 return;
751 }
752 $src_bottom = $src_top + $input{height};
753 }
754 else {
755 $src_bottom = $b;
756 }
757 }
758
759 $src_right > $r and $src_right = $r;
35029411 760 $src_bottom > $b and $src_bottom = $b;
92bda632
TC
761
762 if ($src_right <= $src_left
763 || $src_bottom < $src_top) {
764 $self->_set_error("nothing to paste");
765 return;
766 }
02d1d628
AMH
767
768 i_copyto($self->{IMG}, $src->{IMG},
92bda632
TC
769 $src_left, $src_top, $src_right, $src_bottom,
770 $input{left}, $input{top});
771
02d1d628
AMH
772 return $self; # What should go here??
773}
774
775# Crop an image - i.e. return a new image that is smaller
776
777sub crop {
778 my $self=shift;
779 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
676d5bb5 780
34b3f7e6
TC
781 unless (defined wantarray) {
782 my @caller = caller;
783 warn "crop() called in void context - crop() returns the cropped image at $caller[1] line $caller[2]\n";
784 return;
785 }
786
676d5bb5 787 my %hsh=@_;
299a3866 788
676d5bb5
TC
789 my ($w, $h, $l, $r, $b, $t) =
790 @hsh{qw(width height left right bottom top)};
299a3866 791
676d5bb5
TC
792 # work through the various possibilities
793 if (defined $l) {
794 if (defined $w) {
795 $r = $l + $w;
796 }
797 elsif (!defined $r) {
798 $r = $self->getwidth;
799 }
800 }
801 elsif (defined $r) {
802 if (defined $w) {
803 $l = $r - $w;
804 }
805 else {
806 $l = 0;
807 }
808 }
809 elsif (defined $w) {
810 $l = int(0.5+($self->getwidth()-$w)/2);
811 $r = $l + $w;
812 }
813 else {
814 $l = 0;
815 $r = $self->getwidth;
816 }
817 if (defined $t) {
818 if (defined $h) {
819 $b = $t + $h;
820 }
821 elsif (!defined $b) {
822 $b = $self->getheight;
823 }
824 }
825 elsif (defined $b) {
826 if (defined $h) {
827 $t = $b - $h;
828 }
829 else {
830 $t = 0;
831 }
832 }
833 elsif (defined $h) {
834 $t=int(0.5+($self->getheight()-$h)/2);
835 $b=$t+$h;
836 }
837 else {
838 $t = 0;
839 $b = $self->getheight;
840 }
02d1d628
AMH
841
842 ($l,$r)=($r,$l) if $l>$r;
843 ($t,$b)=($b,$t) if $t>$b;
844
676d5bb5
TC
845 $l < 0 and $l = 0;
846 $r > $self->getwidth and $r = $self->getwidth;
847 $t < 0 and $t = 0;
848 $b > $self->getheight and $b = $self->getheight;
02d1d628 849
676d5bb5
TC
850 if ($l == $r || $t == $b) {
851 $self->_set_error("resulting image would have no content");
852 return;
853 }
9fc9d0ca
TC
854 if( $r < $l or $b < $t ) {
855 $self->_set_error("attempting to crop outside of the image");
856 return;
857 }
676d5bb5 858 my $dst = $self->_sametype(xsize=>$r-$l, ysize=>$b-$t);
02d1d628
AMH
859
860 i_copyto($dst->{IMG},$self->{IMG},$l,$t,$r,$b,0,0);
861 return $dst;
862}
863
ec76939c
TC
864sub _sametype {
865 my ($self, %opts) = @_;
866
867 $self->{IMG} or return $self->_set_error("Not a valid image");
868
869 my $x = $opts{xsize} || $self->getwidth;
870 my $y = $opts{ysize} || $self->getheight;
871 my $channels = $opts{channels} || $self->getchannels;
872
873 my $out = Imager->new;
874 if ($channels == $self->getchannels) {
875 $out->{IMG} = i_sametype($self->{IMG}, $x, $y);
876 }
877 else {
878 $out->{IMG} = i_sametype_chans($self->{IMG}, $x, $y, $channels);
879 }
880 unless ($out->{IMG}) {
881 $self->{ERRSTR} = $self->_error_as_msg;
882 return;
883 }
884
885 return $out;
886}
887
02d1d628
AMH
888# Sets an image to a certain size and channel number
889# if there was previously data in the image it is discarded
890
891sub img_set {
892 my $self=shift;
893
faa9b3e7 894 my %hsh=(xsize=>100, ysize=>100, channels=>3, bits=>8, type=>'direct', @_);
02d1d628
AMH
895
896 if (defined($self->{IMG})) {
faa9b3e7
TC
897 # let IIM_DESTROY destroy it, it's possible this image is
898 # referenced from a virtual image (like masked)
899 #i_img_destroy($self->{IMG});
02d1d628
AMH
900 undef($self->{IMG});
901 }
902
faa9b3e7
TC
903 if ($hsh{type} eq 'paletted' || $hsh{type} eq 'pseudo') {
904 $self->{IMG} = i_img_pal_new($hsh{xsize}, $hsh{ysize}, $hsh{channels},
905 $hsh{maxcolors} || 256);
906 }
365ea842
TC
907 elsif ($hsh{bits} eq 'double') {
908 $self->{IMG} = i_img_double_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
909 }
faa9b3e7
TC
910 elsif ($hsh{bits} == 16) {
911 $self->{IMG} = i_img_16_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
912 }
913 else {
914 $self->{IMG}=Imager::ImgRaw::new($hsh{'xsize'}, $hsh{'ysize'},
915 $hsh{'channels'});
916 }
1501d9b3
TC
917
918 unless ($self->{IMG}) {
919 $self->{ERRSTR} = Imager->_error_as_msg();
920 return;
921 }
922
923 $self;
faa9b3e7
TC
924}
925
926# created a masked version of the current image
927sub masked {
928 my $self = shift;
929
930 $self or return undef;
931 my %opts = (left => 0,
932 top => 0,
933 right => $self->getwidth,
934 bottom => $self->getheight,
935 @_);
936 my $mask = $opts{mask} ? $opts{mask}{IMG} : undef;
937
938 my $result = Imager->new;
939 $result->{IMG} = i_img_masked_new($self->{IMG}, $mask, $opts{left},
940 $opts{top}, $opts{right} - $opts{left},
941 $opts{bottom} - $opts{top});
416e9814
TC
942 unless ($result->{IMG}) {
943 $self->_set_error(Imager->_error_as_msg);
944 return;
945 }
946
faa9b3e7
TC
947 # keep references to the mask and base images so they don't
948 # disappear on us
949 $result->{DEPENDS} = [ $self->{IMG}, $mask ];
950
416e9814 951 return $result;
faa9b3e7
TC
952}
953
954# convert an RGB image into a paletted image
955sub to_paletted {
956 my $self = shift;
957 my $opts;
958 if (@_ != 1 && !ref $_[0]) {
959 $opts = { @_ };
960 }
961 else {
962 $opts = shift;
963 }
964
34b3f7e6
TC
965 unless (defined wantarray) {
966 my @caller = caller;
967 warn "to_paletted() called in void context - to_paletted() returns the converted image at $caller[1] line $caller[2]\n";
968 return;
969 }
970
faa9b3e7
TC
971 my $result = Imager->new;
972 $result->{IMG} = i_img_to_pal($self->{IMG}, $opts);
973
974 #print "Type ", i_img_type($result->{IMG}), "\n";
975
1501d9b3
TC
976 if ($result->{IMG}) {
977 return $result;
978 }
979 else {
980 $self->{ERRSTR} = $self->_error_as_msg;
981 return;
982 }
faa9b3e7
TC
983}
984
985# convert a paletted (or any image) to an 8-bit/channel RGB images
986sub to_rgb8 {
987 my $self = shift;
988 my $result;
989
34b3f7e6
TC
990 unless (defined wantarray) {
991 my @caller = caller;
b13bf7e8 992 warn "to_rgb8() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
34b3f7e6
TC
993 return;
994 }
995
faa9b3e7
TC
996 if ($self->{IMG}) {
997 $result = Imager->new;
998 $result->{IMG} = i_img_to_rgb($self->{IMG})
999 or undef $result;
1000 }
1001
1002 return $result;
1003}
1004
837a4b43
TC
1005# convert a paletted (or any image) to an 8-bit/channel RGB images
1006sub to_rgb16 {
1007 my $self = shift;
1008 my $result;
1009
1010 unless (defined wantarray) {
1011 my @caller = caller;
1012 warn "to_rgb16() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
1013 return;
1014 }
1015
1016 if ($self->{IMG}) {
1017 $result = Imager->new;
1018 $result->{IMG} = i_img_to_rgb16($self->{IMG})
1019 or undef $result;
1020 }
1021
1022 return $result;
1023}
1024
faa9b3e7
TC
1025sub addcolors {
1026 my $self = shift;
1027 my %opts = (colors=>[], @_);
1028
32b97571
TC
1029 unless ($self->{IMG}) {
1030 $self->_set_error("empty input image");
1031 return;
1032 }
1033
1034 my @colors = @{$opts{colors}}
1035 or return undef;
faa9b3e7 1036
32b97571
TC
1037 for my $color (@colors) {
1038 $color = _color($color);
1039 unless ($color) {
1040 $self->_set_error($Imager::ERRSTR);
1041 return;
1042 }
1043 }
1044
1045 return i_addcolors($self->{IMG}, @colors);
faa9b3e7
TC
1046}
1047
1048sub setcolors {
1049 my $self = shift;
1050 my %opts = (start=>0, colors=>[], @_);
faa9b3e7 1051
32b97571
TC
1052 unless ($self->{IMG}) {
1053 $self->_set_error("empty input image");
1054 return;
1055 }
1056
1057 my @colors = @{$opts{colors}}
1058 or return undef;
1059
1060 for my $color (@colors) {
1061 $color = _color($color);
1062 unless ($color) {
1063 $self->_set_error($Imager::ERRSTR);
1064 return;
1065 }
1066 }
1067
1068 return i_setcolors($self->{IMG}, $opts{start}, @colors);
faa9b3e7
TC
1069}
1070
1071sub getcolors {
1072 my $self = shift;
1073 my %opts = @_;
1074 if (!exists $opts{start} && !exists $opts{count}) {
1075 # get them all
1076 $opts{start} = 0;
1077 $opts{count} = $self->colorcount;
1078 }
1079 elsif (!exists $opts{count}) {
1080 $opts{count} = 1;
1081 }
1082 elsif (!exists $opts{start}) {
1083 $opts{start} = 0;
1084 }
1085
1086 $self->{IMG} and
1087 return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
1088}
1089
1090sub colorcount {
1091 i_colorcount($_[0]{IMG});
1092}
1093
1094sub maxcolors {
1095 i_maxcolors($_[0]{IMG});
1096}
1097
1098sub findcolor {
1099 my $self = shift;
1100 my %opts = @_;
1101 $opts{color} or return undef;
1102
1103 $self->{IMG} and i_findcolor($self->{IMG}, $opts{color});
1104}
1105
1106sub bits {
1107 my $self = shift;
af3c2450
TC
1108 my $bits = $self->{IMG} && i_img_bits($self->{IMG});
1109 if ($bits && $bits == length(pack("d", 1)) * 8) {
1110 $bits = 'double';
1111 }
1112 $bits;
faa9b3e7
TC
1113}
1114
1115sub type {
1116 my $self = shift;
1117 if ($self->{IMG}) {
1118 return i_img_type($self->{IMG}) ? "paletted" : "direct";
1119 }
1120}
1121
1122sub virtual {
1123 my $self = shift;
1124 $self->{IMG} and i_img_virtual($self->{IMG});
1125}
1126
bd8052a6
TC
1127sub is_bilevel {
1128 my ($self) = @_;
1129
1130 $self->{IMG} or return;
1131
1132 return i_img_is_monochrome($self->{IMG});
1133}
1134
faa9b3e7
TC
1135sub tags {
1136 my ($self, %opts) = @_;
1137
1138 $self->{IMG} or return;
1139
1140 if (defined $opts{name}) {
1141 my @result;
1142 my $start = 0;
1143 my $found;
1144 while (defined($found = i_tags_find($self->{IMG}, $opts{name}, $start))) {
1145 push @result, (i_tags_get($self->{IMG}, $found))[1];
1146 $start = $found+1;
1147 }
1148 return wantarray ? @result : $result[0];
1149 }
1150 elsif (defined $opts{code}) {
1151 my @result;
1152 my $start = 0;
1153 my $found;
1154 while (defined($found = i_tags_findn($self->{IMG}, $opts{code}, $start))) {
1155 push @result, (i_tags_get($self->{IMG}, $found))[1];
1156 $start = $found+1;
1157 }
1158 return @result;
1159 }
1160 else {
1161 if (wantarray) {
1162 return map { [ i_tags_get($self->{IMG}, $_) ] } 0.. i_tags_count($self->{IMG})-1;
1163 }
1164 else {
1165 return i_tags_count($self->{IMG});
1166 }
1167 }
1168}
1169
1170sub addtag {
1171 my $self = shift;
1172 my %opts = @_;
1173
1174 return -1 unless $self->{IMG};
1175 if ($opts{name}) {
1176 if (defined $opts{value}) {
1177 if ($opts{value} =~ /^\d+$/) {
1178 # add as a number
1179 return i_tags_addn($self->{IMG}, $opts{name}, 0, $opts{value});
1180 }
1181 else {
1182 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{value}, 0);
1183 }
1184 }
1185 elsif (defined $opts{data}) {
1186 # force addition as a string
1187 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{data}, 0);
1188 }
1189 else {
1190 $self->{ERRSTR} = "No value supplied";
1191 return undef;
1192 }
1193 }
1194 elsif ($opts{code}) {
1195 if (defined $opts{value}) {
1196 if ($opts{value} =~ /^\d+$/) {
1197 # add as a number
1198 return i_tags_addn($self->{IMG}, $opts{code}, 0, $opts{value});
1199 }
1200 else {
1201 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{value}, 0);
1202 }
1203 }
1204 elsif (defined $opts{data}) {
1205 # force addition as a string
1206 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{data}, 0);
1207 }
1208 else {
1209 $self->{ERRSTR} = "No value supplied";
1210 return undef;
1211 }
1212 }
1213 else {
1214 return undef;
1215 }
1216}
1217
1218sub deltag {
1219 my $self = shift;
1220 my %opts = @_;
1221
1222 return 0 unless $self->{IMG};
1223
9d540150
TC
1224 if (defined $opts{'index'}) {
1225 return i_tags_delete($self->{IMG}, $opts{'index'});
faa9b3e7
TC
1226 }
1227 elsif (defined $opts{name}) {
1228 return i_tags_delbyname($self->{IMG}, $opts{name});
1229 }
1230 elsif (defined $opts{code}) {
1231 return i_tags_delbycode($self->{IMG}, $opts{code});
1232 }
1233 else {
1234 $self->{ERRSTR} = "Need to supply index, name, or code parameter";
1235 return 0;
1236 }
02d1d628
AMH
1237}
1238
97c4effc
TC
1239sub settag {
1240 my ($self, %opts) = @_;
1241
1242 if ($opts{name}) {
1243 $self->deltag(name=>$opts{name});
1244 return $self->addtag(name=>$opts{name}, value=>$opts{value});
1245 }
1246 elsif (defined $opts{code}) {
1247 $self->deltag(code=>$opts{code});
1248 return $self->addtag(code=>$opts{code}, value=>$opts{value});
1249 }
1250 else {
1251 return undef;
1252 }
1253}
1254
10461f9a
TC
1255
1256sub _get_reader_io {
84e51293 1257 my ($self, $input) = @_;
10461f9a 1258
e7ff1cf7
TC
1259 if ($input->{io}) {
1260 return $input->{io}, undef;
1261 }
84e51293 1262 elsif ($input->{fd}) {
10461f9a
TC
1263 return io_new_fd($input->{fd});
1264 }
1265 elsif ($input->{fh}) {
1266 my $fd = fileno($input->{fh});
de470892 1267 unless (defined $fd) {
10461f9a
TC
1268 $self->_set_error("Handle in fh option not opened");
1269 return;
1270 }
1271 return io_new_fd($fd);
1272 }
1273 elsif ($input->{file}) {
1274 my $file = IO::File->new($input->{file}, "r");
1275 unless ($file) {
1276 $self->_set_error("Could not open $input->{file}: $!");
1277 return;
1278 }
1279 binmode $file;
1280 return (io_new_fd(fileno($file)), $file);
1281 }
1282 elsif ($input->{data}) {
1283 return io_new_buffer($input->{data});
1284 }
1285 elsif ($input->{callback} || $input->{readcb}) {
84e51293
AMH
1286 if (!$input->{seekcb}) {
1287 $self->_set_error("Need a seekcb parameter");
10461f9a
TC
1288 }
1289 if ($input->{maxbuffer}) {
1290 return io_new_cb($input->{writecb},
1291 $input->{callback} || $input->{readcb},
1292 $input->{seekcb}, $input->{closecb},
1293 $input->{maxbuffer});
1294 }
1295 else {
1296 return io_new_cb($input->{writecb},
1297 $input->{callback} || $input->{readcb},
1298 $input->{seekcb}, $input->{closecb});
1299 }
1300 }
1301 else {
1302 $self->_set_error("file/fd/fh/data/callback parameter missing");
1303 return;
1304 }
1305}
1306
1307sub _get_writer_io {
1308 my ($self, $input, $type) = @_;
1309
e7ff1cf7
TC
1310 if ($input->{io}) {
1311 return $input->{io};
1312 }
1313 elsif ($input->{fd}) {
10461f9a
TC
1314 return io_new_fd($input->{fd});
1315 }
1316 elsif ($input->{fh}) {
1317 my $fd = fileno($input->{fh});
de470892 1318 unless (defined $fd) {
10461f9a
TC
1319 $self->_set_error("Handle in fh option not opened");
1320 return;
1321 }
9d1c4956
TC
1322 # flush it
1323 my $oldfh = select($input->{fh});
1324 # flush anything that's buffered, and make sure anything else is flushed
1325 $| = 1;
1326 select($oldfh);
10461f9a
TC
1327 return io_new_fd($fd);
1328 }
1329 elsif ($input->{file}) {
1330 my $fh = new IO::File($input->{file},"w+");
1331 unless ($fh) {
1332 $self->_set_error("Could not open file $input->{file}: $!");
1333 return;
1334 }
1335 binmode($fh) or die;
1336 return (io_new_fd(fileno($fh)), $fh);
1337 }
1338 elsif ($input->{data}) {
1339 return io_new_bufchain();
1340 }
1341 elsif ($input->{callback} || $input->{writecb}) {
1342 if ($input->{maxbuffer}) {
1343 return io_new_cb($input->{callback} || $input->{writecb},
1344 $input->{readcb},
1345 $input->{seekcb}, $input->{closecb},
1346 $input->{maxbuffer});
1347 }
1348 else {
1349 return io_new_cb($input->{callback} || $input->{writecb},
1350 $input->{readcb},
1351 $input->{seekcb}, $input->{closecb});
1352 }
1353 }
1354 else {
1355 $self->_set_error("file/fd/fh/data/callback parameter missing");
1356 return;
1357 }
1358}
1359
02d1d628
AMH
1360# Read an image from file
1361
1362sub read {
1363 my $self = shift;
1364 my %input=@_;
02d1d628
AMH
1365
1366 if (defined($self->{IMG})) {
faa9b3e7
TC
1367 # let IIM_DESTROY do the destruction, since the image may be
1368 # referenced from elsewhere
1369 #i_img_destroy($self->{IMG});
02d1d628
AMH
1370 undef($self->{IMG});
1371 }
1372
84e51293
AMH
1373 my ($IO, $fh) = $self->_get_reader_io(\%input) or return;
1374
10461f9a 1375 unless ($input{'type'}) {
66614d6e
TC
1376 $input{'type'} = i_test_format_probe($IO, -1);
1377 }
84e51293
AMH
1378
1379 unless ($input{'type'}) {
1380 $self->_set_error('type parameter missing and not possible to guess from extension');
10461f9a
TC
1381 return undef;
1382 }
02d1d628 1383
53a6bbd4
TC
1384 _reader_autoload($input{type});
1385
1386 if ($readers{$input{type}} && $readers{$input{type}}{single}) {
1387 return $readers{$input{type}}{single}->($self, $IO, %input);
1388 }
1389
1d7e3124 1390 unless ($formats_low{$input{'type'}}) {
f245645a
TC
1391 my $read_types = join ', ', sort Imager->read_types();
1392 $self->_set_error("format '$input{'type'}' not supported - formats $read_types available for reading");
66614d6e
TC
1393 return;
1394 }
1395
d87dc9a4
TC
1396 my $allow_incomplete = $input{allow_incomplete};
1397 defined $allow_incomplete or $allow_incomplete = 0;
9c106321 1398
2fe0b227 1399 if ( $input{'type'} eq 'pnm' ) {
d87dc9a4 1400 $self->{IMG}=i_readpnm_wiol( $IO, $allow_incomplete );
2fe0b227 1401 if ( !defined($self->{IMG}) ) {
2691d220
TC
1402 $self->{ERRSTR}='unable to read pnm image: '._error_as_msg();
1403 return undef;
790923a4 1404 }
2fe0b227
AMH
1405 $self->{DEBUG} && print "loading a pnm file\n";
1406 return $self;
1407 }
790923a4 1408
2fe0b227 1409 if ( $input{'type'} eq 'bmp' ) {
d87dc9a4 1410 $self->{IMG}=i_readbmp_wiol( $IO, $allow_incomplete );
2fe0b227
AMH
1411 if ( !defined($self->{IMG}) ) {
1412 $self->{ERRSTR}=$self->_error_as_msg();
1413 return undef;
10461f9a 1414 }
2fe0b227
AMH
1415 $self->{DEBUG} && print "loading a bmp file\n";
1416 }
10461f9a 1417
2fe0b227
AMH
1418 if ( $input{'type'} eq 'gif' ) {
1419 if ($input{colors} && !ref($input{colors})) {
1420 # must be a reference to a scalar that accepts the colour map
1421 $self->{ERRSTR} = "option 'colors' must be a scalar reference";
1422 return undef;
1ec86afa 1423 }
f1adece7
TC
1424 if ($input{'gif_consolidate'}) {
1425 if ($input{colors}) {
1426 my $colors;
1427 ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
1428 if ($colors) {
1429 ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
1430 }
1431 }
1432 else {
1433 $self->{IMG} =i_readgif_wiol( $IO );
737a830c 1434 }
737a830c 1435 }
2fe0b227 1436 else {
f1adece7
TC
1437 my $page = $input{'page'};
1438 defined $page or $page = 0;
1439 $self->{IMG} = i_readgif_single_wiol( $IO, $page );
a5dbf458 1440 if ($self->{IMG} && $input{colors}) {
f1adece7
TC
1441 ${ $input{colors} } =
1442 [ i_getcolors($self->{IMG}, 0, i_colorcount($self->{IMG})) ];
1443 }
895dbd34 1444 }
f1adece7 1445
2fe0b227
AMH
1446 if ( !defined($self->{IMG}) ) {
1447 $self->{ERRSTR}=$self->_error_as_msg();
1448 return undef;
895dbd34 1449 }
2fe0b227
AMH
1450 $self->{DEBUG} && print "loading a gif file\n";
1451 }
895dbd34 1452
2fe0b227
AMH
1453 if ( $input{'type'} eq 'tga' ) {
1454 $self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
1455 if ( !defined($self->{IMG}) ) {
1456 $self->{ERRSTR}=$self->_error_as_msg();
1457 return undef;
895dbd34 1458 }
2fe0b227
AMH
1459 $self->{DEBUG} && print "loading a tga file\n";
1460 }
02d1d628 1461
2fe0b227 1462 if ( $input{'type'} eq 'raw' ) {
500888da
TC
1463 unless ( $input{xsize} && $input{ysize} ) {
1464 $self->_set_error('missing xsize or ysize parameter for raw');
2fe0b227 1465 return undef;
895dbd34
AMH
1466 }
1467
500888da
TC
1468 my $interleave = _first($input{raw_interleave}, $input{interleave});
1469 unless (defined $interleave) {
1470 my @caller = caller;
1471 warn "read(type => 'raw') $caller[2] line $caller[1]: supply interleave or raw_interleave for future compatibility\n";
1472 $interleave = 1;
1473 }
1474 my $data_ch = _first($input{raw_datachannels}, $input{datachannels}, 3);
1475 my $store_ch = _first($input{raw_storechannels}, $input{storechannels}, 3);
1476
2fe0b227 1477 $self->{IMG} = i_readraw_wiol( $IO,
500888da
TC
1478 $input{xsize},
1479 $input{ysize},
1480 $data_ch,
1481 $store_ch,
1482 $interleave);
2fe0b227 1483 if ( !defined($self->{IMG}) ) {
5f8f8e17 1484 $self->{ERRSTR}=$self->_error_as_msg();
2fe0b227 1485 return undef;
dd55acc8 1486 }
2fe0b227 1487 $self->{DEBUG} && print "loading a raw file\n";
02d1d628 1488 }
2fe0b227 1489
02d1d628 1490 return $self;
02d1d628
AMH
1491}
1492
53a6bbd4
TC
1493sub register_reader {
1494 my ($class, %opts) = @_;
1495
1496 defined $opts{type}
1497 or die "register_reader called with no type parameter\n";
1498
1499 my $type = $opts{type};
1500
1501 defined $opts{single} || defined $opts{multiple}
1502 or die "register_reader called with no single or multiple parameter\n";
1503
1504 $readers{$type} = { };
1505 if ($opts{single}) {
1506 $readers{$type}{single} = $opts{single};
1507 }
1508 if ($opts{multiple}) {
1509 $readers{$type}{multiple} = $opts{multiple};
1510 }
1511
1512 return 1;
1513}
1514
2b405c9e
TC
1515sub register_writer {
1516 my ($class, %opts) = @_;
1517
1518 defined $opts{type}
1519 or die "register_writer called with no type parameter\n";
1520
1521 my $type = $opts{type};
1522
1523 defined $opts{single} || defined $opts{multiple}
1524 or die "register_writer called with no single or multiple parameter\n";
1525
1526 $writers{$type} = { };
1527 if ($opts{single}) {
1528 $writers{$type}{single} = $opts{single};
1529 }
1530 if ($opts{multiple}) {
1531 $writers{$type}{multiple} = $opts{multiple};
1532 }
1533
1534 return 1;
1535}
1536
f245645a
TC
1537sub read_types {
1538 my %types =
1539 (
1540 map { $_ => 1 }
1541 keys %readers,
1542 grep($file_formats{$_}, keys %formats),
1543 qw(ico sgi), # formats not handled directly, but supplied with Imager
1544 );
1545
1546 return keys %types;
1547}
1548
1549sub write_types {
1550 my %types =
1551 (
1552 map { $_ => 1 }
1553 keys %writers,
1554 grep($file_formats{$_}, keys %formats),
1555 qw(ico sgi), # formats not handled directly, but supplied with Imager
1556 );
1557
1558 return keys %types;
1559}
1560
53a6bbd4
TC
1561# probes for an Imager::File::whatever module
1562sub _reader_autoload {
1563 my $type = shift;
1564
1d7e3124 1565 return if $formats_low{$type} || $readers{$type};
53a6bbd4
TC
1566
1567 return unless $type =~ /^\w+$/;
1568
1569 my $file = "Imager/File/\U$type\E.pm";
1570
1571 unless ($attempted_to_load{$file}) {
1572 eval {
1573 ++$attempted_to_load{$file};
1574 require $file;
1575 };
2b405c9e
TC
1576 if ($@) {
1577 # try to get a reader specific module
1578 my $file = "Imager/File/\U$type\EReader.pm";
1579 unless ($attempted_to_load{$file}) {
1580 eval {
1581 ++$attempted_to_load{$file};
1582 require $file;
1583 };
1584 }
1585 }
1586 }
1587}
1588
1589# probes for an Imager::File::whatever module
1590sub _writer_autoload {
1591 my $type = shift;
1592
1d7e3124 1593 return if $formats_low{$type} || $readers{$type};
2b405c9e
TC
1594
1595 return unless $type =~ /^\w+$/;
1596
1597 my $file = "Imager/File/\U$type\E.pm";
1598
1599 unless ($attempted_to_load{$file}) {
1600 eval {
1601 ++$attempted_to_load{$file};
1602 require $file;
1603 };
1604 if ($@) {
1605 # try to get a writer specific module
1606 my $file = "Imager/File/\U$type\EWriter.pm";
1607 unless ($attempted_to_load{$file}) {
1608 eval {
1609 ++$attempted_to_load{$file};
1610 require $file;
1611 };
1612 }
1613 }
53a6bbd4
TC
1614 }
1615}
1616
97c4effc
TC
1617sub _fix_gif_positions {
1618 my ($opts, $opt, $msg, @imgs) = @_;
2fe0b227 1619
97c4effc
TC
1620 my $positions = $opts->{'gif_positions'};
1621 my $index = 0;
1622 for my $pos (@$positions) {
1623 my ($x, $y) = @$pos;
1624 my $img = $imgs[$index++];
9d1c4956
TC
1625 $img->settag(name=>'gif_left', value=>$x);
1626 $img->settag(name=>'gif_top', value=>$y) if defined $y;
97c4effc
TC
1627 }
1628 $$msg .= "replaced with the gif_left and gif_top tags";
1629}
1630
1631my %obsolete_opts =
1632 (
1633 gif_each_palette=>'gif_local_map',
1634 interlace => 'gif_interlace',
1635 gif_delays => 'gif_delay',
1636 gif_positions => \&_fix_gif_positions,
1637 gif_loop_count => 'gif_loop',
1638 );
1639
6e4af7d4
TC
1640# options that should be converted to colors
1641my %color_opts = map { $_ => 1 } qw/i_background/;
1642
97c4effc
TC
1643sub _set_opts {
1644 my ($self, $opts, $prefix, @imgs) = @_;
1645
1646 for my $opt (keys %$opts) {
1647 my $tagname = $opt;
1648 if ($obsolete_opts{$opt}) {
1649 my $new = $obsolete_opts{$opt};
1650 my $msg = "Obsolete option $opt ";
1651 if (ref $new) {
1652 $new->($opts, $opt, \$msg, @imgs);
1653 }
1654 else {
1655 $msg .= "replaced with the $new tag ";
1656 $tagname = $new;
1657 }
1658 $msg .= "line ".(caller(2))[2]." of file ".(caller(2))[1];
1659 warn $msg if $warn_obsolete && $^W;
1660 }
1661 next unless $tagname =~ /^\Q$prefix/;
1662 my $value = $opts->{$opt};
6e4af7d4
TC
1663 if ($color_opts{$opt}) {
1664 $value = _color($value);
1665 unless ($value) {
1666 $self->_set_error($Imager::ERRSTR);
1667 return;
1668 }
1669 }
97c4effc
TC
1670 if (ref $value) {
1671 if (UNIVERSAL::isa($value, "Imager::Color")) {
1672 my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
1673 for my $img (@imgs) {
1674 $img->settag(name=>$tagname, value=>$tag);
1675 }
1676 }
1677 elsif (ref($value) eq 'ARRAY') {
1678 for my $i (0..$#$value) {
1679 my $val = $value->[$i];
1680 if (ref $val) {
1681 if (UNIVERSAL::isa($val, "Imager::Color")) {
1682 my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
1683 $i < @imgs and
1684 $imgs[$i]->settag(name=>$tagname, value=>$tag);
1685 }
1686 else {
1687 $self->_set_error("Unknown reference type " . ref($value) .
1688 " supplied in array for $opt");
1689 return;
1690 }
1691 }
1692 else {
1693 $i < @imgs
1694 and $imgs[$i]->settag(name=>$tagname, value=>$val);
1695 }
1696 }
1697 }
1698 else {
1699 $self->_set_error("Unknown reference type " . ref($value) .
1700 " supplied for $opt");
1701 return;
1702 }
1703 }
1704 else {
1705 # set it as a tag for every image
1706 for my $img (@imgs) {
1707 $img->settag(name=>$tagname, value=>$value);
1708 }
1709 }
1710 }
1711
1712 return 1;
1713}
1714
02d1d628 1715# Write an image to file
02d1d628
AMH
1716sub write {
1717 my $self = shift;
2fe0b227
AMH
1718 my %input=(jpegquality=>75,
1719 gifquant=>'mc',
1720 lmdither=>6.0,
febba01f
AMH
1721 lmfixed=>[],
1722 idstring=>"",
1723 compress=>1,
1724 wierdpack=>0,
4c2d6970 1725 fax_fine=>1, @_);
10461f9a 1726 my $rc;
02d1d628 1727
97c4effc
TC
1728 $self->_set_opts(\%input, "i_", $self)
1729 or return undef;
1730
02d1d628
AMH
1731 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1732
9d540150
TC
1733 if (!$input{'type'} and $input{file}) {
1734 $input{'type'}=$FORMATGUESS->($input{file});
1735 }
1736 if (!$input{'type'}) {
1737 $self->{ERRSTR}='type parameter missing and not possible to guess from extension';
1738 return undef;
1739 }
02d1d628 1740
2b405c9e 1741 _writer_autoload($input{type});
02d1d628 1742
2b405c9e
TC
1743 my ($IO, $fh);
1744 if ($writers{$input{type}} && $writers{$input{type}}{single}) {
1745 ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
2fe0b227 1746 or return undef;
febba01f 1747
2b405c9e 1748 $writers{$input{type}}{single}->($self, $IO, %input)
2fe0b227 1749 or return undef;
2b405c9e
TC
1750 }
1751 else {
1d7e3124 1752 if (!$formats_low{$input{'type'}}) {
f245645a
TC
1753 my $write_types = join ', ', sort Imager->write_types();
1754 $self->_set_error("format '$input{'type'}' not supported - formats $write_types available for writing");
2fe0b227 1755 return undef;
930c67c8 1756 }
2b405c9e
TC
1757
1758 ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
2fe0b227 1759 or return undef;
2b405c9e 1760
e5ee047b 1761 if ( $input{'type'} eq 'pnm' ) {
2b405c9e
TC
1762 $self->_set_opts(\%input, "pnm_", $self)
1763 or return undef;
1764 if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
1765 $self->{ERRSTR} = $self->_error_as_msg();
1766 return undef;
1767 }
1768 $self->{DEBUG} && print "writing a pnm file\n";
1769 } elsif ( $input{'type'} eq 'raw' ) {
1770 $self->_set_opts(\%input, "raw_", $self)
1771 or return undef;
1772 if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
1773 $self->{ERRSTR} = $self->_error_as_msg();
1774 return undef;
1775 }
1776 $self->{DEBUG} && print "writing a raw file\n";
2b405c9e
TC
1777 } elsif ( $input{'type'} eq 'jpeg' ) {
1778 $self->_set_opts(\%input, "jpeg_", $self)
1779 or return undef;
1780 $self->_set_opts(\%input, "exif_", $self)
1781 or return undef;
1782 if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
1783 $self->{ERRSTR} = $self->_error_as_msg();
1784 return undef;
1785 }
1786 $self->{DEBUG} && print "writing a jpeg file\n";
1787 } elsif ( $input{'type'} eq 'bmp' ) {
1788 $self->_set_opts(\%input, "bmp_", $self)
1789 or return undef;
1790 if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
ae12796a 1791 $self->{ERRSTR} = $self->_error_as_msg;
2b405c9e
TC
1792 return undef;
1793 }
1794 $self->{DEBUG} && print "writing a bmp file\n";
1795 } elsif ( $input{'type'} eq 'tga' ) {
1796 $self->_set_opts(\%input, "tga_", $self)
1797 or return undef;
1798
1799 if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
1800 $self->{ERRSTR}=$self->_error_as_msg();
1801 return undef;
1802 }
1803 $self->{DEBUG} && print "writing a tga file\n";
1804 } elsif ( $input{'type'} eq 'gif' ) {
1805 $self->_set_opts(\%input, "gif_", $self)
1806 or return undef;
1807 # compatibility with the old interfaces
1808 if ($input{gifquant} eq 'lm') {
1809 $input{make_colors} = 'addi';
1810 $input{translate} = 'perturb';
1811 $input{perturb} = $input{lmdither};
1812 } elsif ($input{gifquant} eq 'gen') {
1813 # just pass options through
1814 } else {
1815 $input{make_colors} = 'webmap'; # ignored
1816 $input{translate} = 'giflib';
1817 }
1818 if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
1819 $self->{ERRSTR} = $self->_error_as_msg;
1820 return;
1821 }
1501d9b3 1822 }
02d1d628 1823 }
10461f9a 1824
2fe0b227
AMH
1825 if (exists $input{'data'}) {
1826 my $data = io_slurp($IO);
1827 if (!$data) {
1828 $self->{ERRSTR}='Could not slurp from buffer';
1829 return undef;
1830 }
1831 ${$input{data}} = $data;
1832 }
02d1d628
AMH
1833 return $self;
1834}
1835
1836sub write_multi {
1837 my ($class, $opts, @images) = @_;
1838
2b405c9e
TC
1839 my $type = $opts->{type};
1840
1841 if (!$type && $opts->{'file'}) {
1842 $type = $FORMATGUESS->($opts->{'file'});
10461f9a 1843 }
2b405c9e 1844 unless ($type) {
10461f9a
TC
1845 $class->_set_error('type parameter missing and not possible to guess from extension');
1846 return;
1847 }
1848 # translate to ImgRaw
1849 if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
1850 $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
1851 return 0;
1852 }
97c4effc
TC
1853 $class->_set_opts($opts, "i_", @images)
1854 or return;
10461f9a 1855 my @work = map $_->{IMG}, @images;
2b405c9e
TC
1856
1857 _writer_autoload($type);
1858
1859 my ($IO, $file);
1860 if ($writers{$type} && $writers{$type}{multiple}) {
1861 ($IO, $file) = $class->_get_writer_io($opts, $type)
1862 or return undef;
1863
1864 $writers{$type}{multiple}->($class, $IO, $opts, @images)
1865 or return undef;
1866 }
1867 else {
1868 if (!$formats{$type}) {
f245645a
TC
1869 my $write_types = join ', ', sort Imager->write_types();
1870 $class->_set_error("format '$type' not supported - formats $write_types available for writing");
2b405c9e
TC
1871 return undef;
1872 }
1873
1874 ($IO, $file) = $class->_get_writer_io($opts, $type)
1875 or return undef;
1876
e5ee047b 1877 if (0) { # eventually PNM in here, now that TIFF/GIF are elsewhere
02d1d628
AMH
1878 }
1879 else {
e7ff1cf7
TC
1880 if (@images == 1) {
1881 unless ($images[0]->write(%$opts, io => $IO, type => $type)) {
1882 return 1;
1883 }
1884 }
1885 else {
1886 $ERRSTR = "Sorry, write_multi doesn't support $type yet";
1887 return 0;
1888 }
02d1d628
AMH
1889 }
1890 }
2b405c9e
TC
1891
1892 if (exists $opts->{'data'}) {
1893 my $data = io_slurp($IO);
1894 if (!$data) {
1895 Imager->_set_error('Could not slurp from buffer');
1896 return undef;
1897 }
1898 ${$opts->{data}} = $data;
02d1d628 1899 }
2b405c9e 1900 return 1;
02d1d628
AMH
1901}
1902
faa9b3e7
TC
1903# read multiple images from a file
1904sub read_multi {
1905 my ($class, %opts) = @_;
1906
53a6bbd4
TC
1907 my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
1908 or return;
1909
1910 my $type = $opts{'type'};
1911 unless ($type) {
1912 $type = i_test_format_probe($IO, -1);
1913 }
1914
1915 if ($opts{file} && !$type) {
faa9b3e7 1916 # guess the type
53a6bbd4 1917 $type = $FORMATGUESS->($opts{file});
faa9b3e7 1918 }
53a6bbd4
TC
1919
1920 unless ($type) {
faa9b3e7
TC
1921 $ERRSTR = "No type parameter supplied and it couldn't be guessed";
1922 return;
1923 }
faa9b3e7 1924
53a6bbd4
TC
1925 _reader_autoload($type);
1926
1927 if ($readers{$type} && $readers{$type}{multiple}) {
1928 return $readers{$type}{multiple}->($IO, %opts);
1929 }
1930
8d46e5da
TC
1931 unless ($formats{$type}) {
1932 my $read_types = join ', ', sort Imager->read_types();
1933 Imager->_set_error("format '$type' not supported - formats $read_types available for reading");
1934 return;
1935 }
1936
e5ee047b
TC
1937 my @imgs;
1938 if ($type eq 'pnm') {
2086be61 1939 @imgs = i_readpnm_multi_wiol($IO, $opts{allow_incomplete}||0);
faa9b3e7 1940 }
e7ff1cf7
TC
1941 else {
1942 my $img = Imager->new;
1943 if ($img->read(%opts, io => $IO, type => $type)) {
1944 return ( $img );
1945 }
f245645a 1946 Imager->_set_error($img->errstr);
2086be61 1947 return;
e7ff1cf7 1948 }
faa9b3e7 1949
2086be61
TC
1950 if (!@imgs) {
1951 $ERRSTR = _error_as_msg();
faa9b3e7 1952 return;
2086be61
TC
1953 }
1954 return map {
1955 bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
1956 } @imgs;
faa9b3e7
TC
1957}
1958
02d1d628
AMH
1959# Destroy an Imager object
1960
1961sub DESTROY {
1962 my $self=shift;
1963 # delete $instances{$self};
1964 if (defined($self->{IMG})) {
faa9b3e7
TC
1965 # the following is now handled by the XS DESTROY method for
1966 # Imager::ImgRaw object
1967 # Re-enabling this will break virtual images
1968 # tested for in t/t020masked.t
1969 # i_img_destroy($self->{IMG});
02d1d628
AMH
1970 undef($self->{IMG});
1971 } else {
1972# print "Destroy Called on an empty image!\n"; # why did I put this here??
1973 }
1974}
1975
1976# Perform an inplace filter of an image
1977# that is the image will be overwritten with the data
1978
1979sub filter {
1980 my $self=shift;
1981 my %input=@_;
1982 my %hsh;
1983 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1984
9d540150 1985 if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; }
02d1d628 1986
9d540150 1987 if ( (grep { $_ eq $input{'type'} } keys %filters) != 1) {
02d1d628
AMH
1988 $self->{ERRSTR}='type parameter not matching any filter'; return undef;
1989 }
1990
9d540150
TC
1991 if ($filters{$input{'type'}}{names}) {
1992 my $names = $filters{$input{'type'}}{names};
6607600c
TC
1993 for my $name (keys %$names) {
1994 if (defined $input{$name} && exists $names->{$name}{$input{$name}}) {
1995 $input{$name} = $names->{$name}{$input{$name}};
1996 }
1997 }
1998 }
9d540150 1999 if (defined($filters{$input{'type'}}{defaults})) {
7327d4b0
TC
2000 %hsh=( image => $self->{IMG},
2001 imager => $self,
2002 %{$filters{$input{'type'}}{defaults}},
2003 %input );
02d1d628 2004 } else {
7327d4b0
TC
2005 %hsh=( image => $self->{IMG},
2006 imager => $self,
2007 %input );
02d1d628
AMH
2008 }
2009
9d540150 2010 my @cs=@{$filters{$input{'type'}}{callseq}};
02d1d628
AMH
2011
2012 for(@cs) {
2013 if (!defined($hsh{$_})) {
9d540150 2014 $self->{ERRSTR}="missing parameter '$_' for filter ".$input{'type'}; return undef;
02d1d628
AMH
2015 }
2016 }
2017
109bec2d
TC
2018 eval {
2019 local $SIG{__DIE__}; # we don't want this processed by confess, etc
2020 &{$filters{$input{'type'}}{callsub}}(%hsh);
2021 };
2022 if ($@) {
2023 chomp($self->{ERRSTR} = $@);
2024 return;
2025 }
02d1d628
AMH
2026
2027 my @b=keys %hsh;
2028
2029 $self->{DEBUG} && print "callseq is: @cs\n";
2030 $self->{DEBUG} && print "matching callseq is: @b\n";
2031
2032 return $self;
2033}
2034
92bda632
TC
2035sub register_filter {
2036 my $class = shift;
2037 my %hsh = ( defaults => {}, @_ );
2038
2039 defined $hsh{type}
2040 or die "register_filter() with no type\n";
2041 defined $hsh{callsub}
2042 or die "register_filter() with no callsub\n";
2043 defined $hsh{callseq}
2044 or die "register_filter() with no callseq\n";
2045
2046 exists $filters{$hsh{type}}
2047 and return;
2048
2049 $filters{$hsh{type}} = \%hsh;
2050
2051 return 1;
2052}
2053
df9aaafb
TC
2054sub scale_calculate {
2055 my $self = shift;
02d1d628 2056
df9aaafb 2057 my %opts = ('type'=>'max', @_);
4f579313 2058
de470892
TC
2059 # none of these should be references
2060 for my $name (qw/xpixels ypixels xscalefactor yscalefactor width height/) {
2061 if (defined $opts{$name} && ref $opts{$name}) {
2062 $self->_set_error("scale_calculate: $name parameter cannot be a reference");
2063 return;
2064 }
2065 }
2066
df9aaafb
TC
2067 my ($x_scale, $y_scale);
2068 my $width = $opts{width};
2069 my $height = $opts{height};
2070 if (ref $self) {
2071 defined $width or $width = $self->getwidth;
2072 defined $height or $height = $self->getheight;
ace46df2 2073 }
df9aaafb
TC
2074 else {
2075 unless (defined $width && defined $height) {
2076 $self->_set_error("scale_calculate: width and height parameters must be supplied when called as a class method");
2077 return;
2078 }
5168ca3a 2079 }
02d1d628 2080
658f724e
TC
2081 if ($opts{'xscalefactor'} && $opts{'yscalefactor'}) {
2082 $x_scale = $opts{'xscalefactor'};
2083 $y_scale = $opts{'yscalefactor'};
2084 }
2085 elsif ($opts{'xscalefactor'}) {
2086 $x_scale = $opts{'xscalefactor'};
2087 $y_scale = $opts{'scalefactor'} || $x_scale;
2088 }
2089 elsif ($opts{'yscalefactor'}) {
2090 $y_scale = $opts{'yscalefactor'};
2091 $x_scale = $opts{'scalefactor'} || $y_scale;
2092 }
2093 else {
2094 $x_scale = $y_scale = $opts{'scalefactor'} || 0.5;
2095 }
2096
5168ca3a 2097 # work out the scaling
9d540150 2098 if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
df9aaafb
TC
2099 my ($xpix, $ypix)=( $opts{xpixels} / $width ,
2100 $opts{ypixels} / $height );
5168ca3a 2101 if ($opts{'type'} eq 'min') {
658f724e 2102 $x_scale = $y_scale = _min($xpix,$ypix);
5168ca3a
TC
2103 }
2104 elsif ($opts{'type'} eq 'max') {
658f724e
TC
2105 $x_scale = $y_scale = _max($xpix,$ypix);
2106 }
2107 elsif ($opts{'type'} eq 'nonprop' || $opts{'type'} eq 'non-proportional') {
2108 $x_scale = $xpix;
2109 $y_scale = $ypix;
5168ca3a
TC
2110 }
2111 else {
2112 $self->_set_error('invalid value for type parameter');
df9aaafb 2113 return;
5168ca3a
TC
2114 }
2115 } elsif ($opts{xpixels}) {
df9aaafb 2116 $x_scale = $y_scale = $opts{xpixels} / $width;
5168ca3a
TC
2117 }
2118 elsif ($opts{ypixels}) {
df9aaafb 2119 $x_scale = $y_scale = $opts{ypixels}/$height;
5168ca3a 2120 }
41c7d053
TC
2121 elsif ($opts{constrain} && ref $opts{constrain}
2122 && $opts{constrain}->can('constrain')) {
2123 # we've been passed an Image::Math::Constrain object or something
2124 # that looks like one
658f724e 2125 my $scalefactor;
4f579313 2126 (undef, undef, $scalefactor)
41c7d053 2127 = $opts{constrain}->constrain($self->getwidth, $self->getheight);
4f579313 2128 unless ($scalefactor) {
41c7d053 2129 $self->_set_error('constrain method failed on constrain parameter');
df9aaafb 2130 return;
41c7d053 2131 }
658f724e 2132 $x_scale = $y_scale = $scalefactor;
41c7d053 2133 }
02d1d628 2134
df9aaafb
TC
2135 my $new_width = int($x_scale * $width + 0.5);
2136 $new_width > 0 or $new_width = 1;
2137 my $new_height = int($y_scale * $height + 0.5);
2138 $new_height > 0 or $new_height = 1;
2139
2140 return ($x_scale, $y_scale, $new_width, $new_height);
2141
2142}
2143
2144# Scale an image to requested size and return the scaled version
2145
2146sub scale {
2147 my $self=shift;
2148 my %opts = (qtype=>'normal' ,@_);
2149 my $img = Imager->new();
2150 my $tmp = Imager->new();
2151
2152 unless (defined wantarray) {
2153 my @caller = caller;
2154 warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
2155 return;
2156 }
2157
2158 unless ($self->{IMG}) {
2159 $self->_set_error('empty input image');
2160 return undef;
2161 }
2162
2163 my ($x_scale, $y_scale, $new_width, $new_height) =
2164 $self->scale_calculate(%opts)
2165 or return;
2166
02d1d628 2167 if ($opts{qtype} eq 'normal') {
658f724e 2168 $tmp->{IMG} = i_scaleaxis($self->{IMG}, $x_scale, 0);
5168ca3a 2169 if ( !defined($tmp->{IMG}) ) {
de470892 2170 $self->{ERRSTR} = 'unable to scale image: ' . $self->_error_as_msg;
5168ca3a
TC
2171 return undef;
2172 }
658f724e 2173 $img->{IMG}=i_scaleaxis($tmp->{IMG}, $y_scale, 1);
5168ca3a 2174 if ( !defined($img->{IMG}) ) {
de470892 2175 $self->{ERRSTR}='unable to scale image: ' . $self->_error_as_msg;
5168ca3a
TC
2176 return undef;
2177 }
2178
02d1d628
AMH
2179 return $img;
2180 }
5168ca3a 2181 elsif ($opts{'qtype'} eq 'preview') {
658f724e 2182 $img->{IMG} = i_scale_nn($self->{IMG}, $x_scale, $y_scale);
5168ca3a
TC
2183 if ( !defined($img->{IMG}) ) {
2184 $self->{ERRSTR}='unable to scale image';
2185 return undef;
2186 }
02d1d628
AMH
2187 return $img;
2188 }
658f724e 2189 elsif ($opts{'qtype'} eq 'mixing') {
658f724e
TC
2190 $img->{IMG} = i_scale_mixing($self->{IMG}, $new_width, $new_height);
2191 unless ($img->{IMG}) {
de470892 2192 $self->_set_error(Imager->_error_as_msg);
658f724e
TC
2193 return;
2194 }
2195 return $img;
2196 }
5168ca3a
TC
2197 else {
2198 $self->_set_error('invalid value for qtype parameter');
2199 return undef;
2200 }
02d1d628
AMH
2201}
2202
2203# Scales only along the X axis
2204
2205sub scaleX {
15327bf5
TC
2206 my $self = shift;
2207 my %opts = ( scalefactor=>0.5, @_ );
02d1d628 2208
34b3f7e6
TC
2209 unless (defined wantarray) {
2210 my @caller = caller;
2211 warn "scaleX() called in void context - scaleX() returns the scaled image at $caller[1] line $caller[2]\n";
2212 return;
2213 }
2214
15327bf5
TC
2215 unless ($self->{IMG}) {
2216 $self->{ERRSTR} = 'empty input image';
2217 return undef;
2218 }
02d1d628
AMH
2219
2220 my $img = Imager->new();
2221
15327bf5 2222 my $scalefactor = $opts{scalefactor};
02d1d628 2223
15327bf5
TC
2224 if ($opts{pixels}) {
2225 $scalefactor = $opts{pixels} / $self->getwidth();
2226 }
2227
2228 unless ($self->{IMG}) {
2229 $self->{ERRSTR}='empty input image';
2230 return undef;
2231 }
2232
2233 $img->{IMG} = i_scaleaxis($self->{IMG}, $scalefactor, 0);
2234
2235 if ( !defined($img->{IMG}) ) {
2236 $self->{ERRSTR} = 'unable to scale image';
2237 return undef;
2238 }
02d1d628 2239
02d1d628
AMH
2240 return $img;
2241}
2242
2243# Scales only along the Y axis
2244
2245sub scaleY {
15327bf5
TC
2246 my $self = shift;
2247 my %opts = ( scalefactor => 0.5, @_ );
02d1d628 2248
34b3f7e6
TC
2249 unless (defined wantarray) {
2250 my @caller = caller;
2251 warn "scaleY() called in void context - scaleY() returns the scaled image at $caller[1] line $caller[2]\n";
2252 return;
2253 }
2254
02d1d628
AMH
2255 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2256
2257 my $img = Imager->new();
2258
15327bf5 2259 my $scalefactor = $opts{scalefactor};
02d1d628 2260
15327bf5
TC
2261 if ($opts{pixels}) {
2262 $scalefactor = $opts{pixels} / $self->getheight();
2263 }
2264
2265 unless ($self->{IMG}) {
2266 $self->{ERRSTR} = 'empty input image';
2267 return undef;
2268 }
2269 $img->{IMG}=i_scaleaxis($self->{IMG}, $scalefactor, 1);
2270
2271 if ( !defined($img->{IMG}) ) {
2272 $self->{ERRSTR} = 'unable to scale image';
2273 return undef;
2274 }
02d1d628 2275
02d1d628
AMH
2276 return $img;
2277}
2278
02d1d628
AMH
2279# Transform returns a spatial transformation of the input image
2280# this moves pixels to a new location in the returned image.
2281# NOTE - should make a utility function to check transforms for
2282# stack overruns
2283
2284sub transform {
2285 my $self=shift;
2286 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2287 my %opts=@_;
2288 my (@op,@ropx,@ropy,$iop,$or,@parm,$expr,@xt,@yt,@pt,$numre);
2289
2290# print Dumper(\%opts);
2291# xopcopdes
2292
2293 if ( $opts{'xexpr'} and $opts{'yexpr'} ) {
2294 if (!$I2P) {
2295 eval ("use Affix::Infix2Postfix;");
2296 print $@;
2297 if ( $@ ) {
2298 $self->{ERRSTR}='transform: expr given and Affix::Infix2Postfix is not avaliable.';
2299 return undef;
2300 }
2301 $I2P=Affix::Infix2Postfix->new('ops'=>[{op=>'+',trans=>'Add'},
2302 {op=>'-',trans=>'Sub'},
2303 {op=>'*',trans=>'Mult'},
2304 {op=>'/',trans=>'Div'},
9d540150 2305 {op=>'-','type'=>'unary',trans=>'u-'},
02d1d628 2306 {op=>'**'},
9d540150 2307 {op=>'func','type'=>'unary'}],
02d1d628
AMH
2308 'grouping'=>[qw( \( \) )],
2309 'func'=>[qw( sin cos )],
2310 'vars'=>[qw( x y )]
2311 );
2312 }
2313
2314 @xt=$I2P->translate($opts{'xexpr'});
2315 @yt=$I2P->translate($opts{'yexpr'});
2316
2317 $numre=$I2P->{'numre'};
2318 @pt=(0,0);
2319
2320 for(@xt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'xopcodes'}},'Parm',$#pt); } else { push(@{$opts{'xopcodes'}},$_); } }
2321 for(@yt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'yopcodes'}},'Parm',$#pt); } else { push(@{$opts{'yopcodes'}},$_); } }
2322 @{$opts{'parm'}}=@pt;
2323 }
2324
2325# print Dumper(\%opts);
2326
2327 if ( !exists $opts{'xopcodes'} or @{$opts{'xopcodes'}}==0) {
2328 $self->{ERRSTR}='transform: no xopcodes given.';
2329 return undef;
2330 }
2331
2332 @op=@{$opts{'xopcodes'}};
2333 for $iop (@op) {
2334 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
2335 $self->{ERRSTR}="transform: illegal opcode '$_'.";
2336 return undef;
2337 }
2338 push(@ropx,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
2339 }
2340
2341
2342# yopcopdes
2343
2344 if ( !exists $opts{'yopcodes'} or @{$opts{'yopcodes'}}==0) {
2345 $self->{ERRSTR}='transform: no yopcodes given.';
2346 return undef;
2347 }
2348
2349 @op=@{$opts{'yopcodes'}};
2350 for $iop (@op) {
2351 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
2352 $self->{ERRSTR}="transform: illegal opcode '$_'.";
2353 return undef;
2354 }
2355 push(@ropy,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
2356 }
2357
2358#parameters
2359
2360 if ( !exists $opts{'parm'}) {
2361 $self->{ERRSTR}='transform: no parameter arg given.';
2362 return undef;
2363 }
2364
2365# print Dumper(\@ropx);
2366# print Dumper(\@ropy);
2367# print Dumper(\@ropy);
2368
2369 my $img = Imager->new();
2370 $img->{IMG}=i_transform($self->{IMG},\@ropx,\@ropy,$opts{'parm'});
2371 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='transform: failed'; return undef; }
2372 return $img;
2373}
2374
2375
bf94b653
TC
2376sub transform2 {
2377 my ($opts, @imgs) = @_;
2378
2379 require "Imager/Expr.pm";
2380
2381 $opts->{variables} = [ qw(x y) ];
2382 my ($width, $height) = @{$opts}{qw(width height)};
2383 if (@imgs) {
2384 $width ||= $imgs[0]->getwidth();
2385 $height ||= $imgs[0]->getheight();
2386 my $img_num = 1;
2387 for my $img (@imgs) {
2388 $opts->{constants}{"w$img_num"} = $img->getwidth();
2389 $opts->{constants}{"h$img_num"} = $img->getheight();
2390 $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
2391 $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
2392 ++$img_num;
02d1d628 2393 }
02d1d628 2394 }
bf94b653
TC
2395 if ($width) {
2396 $opts->{constants}{w} = $width;
2397 $opts->{constants}{cx} = $width/2;
2398 }
2399 else {
2400 $Imager::ERRSTR = "No width supplied";
2401 return;
2402 }
2403 if ($height) {
2404 $opts->{constants}{h} = $height;
2405 $opts->{constants}{cy} = $height/2;
2406 }
2407 else {
2408 $Imager::ERRSTR = "No height supplied";
2409 return;
2410 }
2411 my $code = Imager::Expr->new($opts);
2412 if (!$code) {
2413 $Imager::ERRSTR = Imager::Expr::error();
2414 return;
2415 }
e5744e01
TC
2416 my $channels = $opts->{channels} || 3;
2417 unless ($channels >= 1 && $channels <= 4) {
2418 return Imager->_set_error("channels must be an integer between 1 and 4");
2419 }
9982a307 2420
bf94b653 2421 my $img = Imager->new();
e5744e01
TC
2422 $img->{IMG} = i_transform2($opts->{width}, $opts->{height},
2423 $channels, $code->code(),
bf94b653
TC
2424 $code->nregs(), $code->cregs(),
2425 [ map { $_->{IMG} } @imgs ]);
2426 if (!defined $img->{IMG}) {
2427 $Imager::ERRSTR = Imager->_error_as_msg();
2428 return;
2429 }
9982a307 2430
bf94b653 2431 return $img;
02d1d628
AMH
2432}
2433
02d1d628
AMH
2434sub rubthrough {
2435 my $self=shift;
9b1ec2b8 2436 my %opts= @_;
02d1d628 2437
e7b95388
TC
2438 unless ($self->{IMG}) {
2439 $self->{ERRSTR}='empty input image';
2440 return undef;
2441 }
2442 unless ($opts{src} && $opts{src}->{IMG}) {
2443 $self->{ERRSTR}='empty input image for src';
2444 return undef;
2445 }
02d1d628 2446
71dc4a83
AMH
2447 %opts = (src_minx => 0,
2448 src_miny => 0,
2449 src_maxx => $opts{src}->getwidth(),
2450 src_maxy => $opts{src}->getheight(),
2451 %opts);
2452
9b1ec2b8
TC
2453 my $tx = $opts{tx};
2454 defined $tx or $tx = $opts{left};
2455 defined $tx or $tx = 0;
2456
2457 my $ty = $opts{ty};
2458 defined $ty or $ty = $opts{top};
2459 defined $ty or $ty = 0;
2460
2461 unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $tx, $ty,
e7b95388
TC
2462 $opts{src_minx}, $opts{src_miny},
2463 $opts{src_maxx}, $opts{src_maxy})) {
2464 $self->_set_error($self->_error_as_msg());
faa9b3e7
TC
2465 return undef;
2466 }
9b1ec2b8 2467
02d1d628
AMH
2468 return $self;
2469}
2470
9b1ec2b8
TC
2471sub compose {
2472 my $self = shift;
2473 my %opts =
2474 (
2475 opacity => 1.0,
2476 mask_left => 0,
2477 mask_top => 0,
2478 @_
2479 );
2480
2481 unless ($self->{IMG}) {
2482 $self->_set_error("compose: empty input image");
2483 return;
2484 }
2485
2486 unless ($opts{src}) {
2487 $self->_set_error("compose: src parameter missing");
2488 return;
2489 }
2490
2491 unless ($opts{src}{IMG}) {
2492 $self->_set_error("compose: src parameter empty image");
2493 return;
2494 }
2495 my $src = $opts{src};
2496
2497 my $left = $opts{left};
2498 defined $left or $left = $opts{tx};
2499 defined $left or $left = 0;
2500
2501 my $top = $opts{top};
2502 defined $top or $top = $opts{ty};
2503 defined $top or $top = 0;
2504
2505 my $src_left = $opts{src_left};
2506 defined $src_left or $src_left = $opts{src_minx};
2507 defined $src_left or $src_left = 0;
2508
2509 my $src_top = $opts{src_top};
2510 defined $src_top or $src_top = $opts{src_miny};
2511 defined $src_top or $src_top = 0;
2512
2513 my $width = $opts{width};
2514 if (!defined $width && defined $opts{src_maxx}) {
2515 $width = $opts{src_maxx} - $src_left;
2516 }
2517 defined $width or $width = $src->getwidth() - $src_left;
2518
2519 my $height = $opts{height};
2520 if (!defined $height && defined $opts{src_maxy}) {
2521 $height = $opts{src_maxy} - $src_top;
2522 }
2523 defined $height or $height = $src->getheight() - $src_top;
2524
2525 my $combine = $self->_combine($opts{combine}, 'normal');
2526
2527 if ($opts{mask}) {
2528 unless ($opts{mask}{IMG}) {
2529 $self->_set_error("compose: mask parameter empty image");
2530 return;
2531 }
2532
2533 my $mask_left = $opts{mask_left};
2534 defined $mask_left or $mask_left = $opts{mask_minx};
2535 defined $mask_left or $mask_left = 0;
2536
2537 my $mask_top = $opts{mask_top};
2538 defined $mask_top or $mask_top = $opts{mask_miny};
2539 defined $mask_top or $mask_top = 0;
2540
618a3282 2541 unless (i_compose_mask($self->{IMG}, $src->{IMG}, $opts{mask}{IMG},
9b1ec2b8
TC
2542 $left, $top, $src_left, $src_top,
2543 $mask_left, $mask_top, $width, $height,
618a3282
TC
2544 $combine, $opts{opacity})) {
2545 $self->_set_error(Imager->_error_as_msg);
2546 return;
2547 }
9b1ec2b8
TC
2548 }
2549 else {
618a3282
TC
2550 unless (i_compose($self->{IMG}, $src->{IMG}, $left, $top, $src_left, $src_top,
2551 $width, $height, $combine, $opts{opacity})) {
2552 $self->_set_error(Imager->_error_as_msg);
2553 return;
2554 }
9b1ec2b8
TC
2555 }
2556
2557 return $self;
2558}
02d1d628 2559
142c26ff
AMH
2560sub flip {
2561 my $self = shift;
2562 my %opts = @_;
9191e525 2563 my %xlate = (h=>0, v=>1, hv=>2, vh=>2);
142c26ff
AMH
2564 my $dir;
2565 return () unless defined $opts{'dir'} and defined $xlate{$opts{'dir'}};
2566 $dir = $xlate{$opts{'dir'}};
2567 return $self if i_flipxy($self->{IMG}, $dir);
2568 return ();
2569}
2570
faa9b3e7
TC
2571sub rotate {
2572 my $self = shift;
2573 my %opts = @_;
34b3f7e6
TC
2574
2575 unless (defined wantarray) {
2576 my @caller = caller;
2577 warn "rotate() called in void context - rotate() returns the rotated image at $caller[1] line $caller[2]\n";
2578 return;
2579 }
2580
faa9b3e7
TC
2581 if (defined $opts{right}) {
2582 my $degrees = $opts{right};
2583 if ($degrees < 0) {
2584 $degrees += 360 * int(((-$degrees)+360)/360);
2585 }
2586 $degrees = $degrees % 360;
2587 if ($degrees == 0) {
2588 return $self->copy();
2589 }
2590 elsif ($degrees == 90 || $degrees == 180 || $degrees == 270) {
2591 my $result = Imager->new();
2592 if ($result->{IMG} = i_rotate90($self->{IMG}, $degrees)) {
2593 return $result;
2594 }
2595 else {
2596 $self->{ERRSTR} = $self->_error_as_msg();
2597 return undef;
2598 }
2599 }
2600 else {
2601 $self->{ERRSTR} = "Parameter 'right' must be a multiple of 90 degrees";
2602 return undef;
2603 }
2604 }
2605 elsif (defined $opts{radians} || defined $opts{degrees}) {
2606 my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
2607
7f627571 2608 my $back = $opts{back};
faa9b3e7 2609 my $result = Imager->new;
7f627571
TC
2610 if ($back) {
2611 $back = _color($back);
2612 unless ($back) {
2613 $self->_set_error(Imager->errstr);
2614 return undef;
2615 }
2616
2617 $result->{IMG} = i_rotate_exact($self->{IMG}, $amount, $back);
0d3b936e
TC
2618 }
2619 else {
2620 $result->{IMG} = i_rotate_exact($self->{IMG}, $amount);
2621 }
2622 if ($result->{IMG}) {
faa9b3e7
TC
2623 return $result;
2624 }
2625 else {
2626 $self->{ERRSTR} = $self->_error_as_msg();
2627 return undef;
2628 }
2629 }
2630 else {
0d3b936e 2631 $self->{ERRSTR} = "Only the 'right', 'radians' and 'degrees' parameters are available";
faa9b3e7
TC
2632 return undef;
2633 }
2634}
2635
2636sub matrix_transform {
2637 my $self = shift;
2638 my %opts = @_;
2639
34b3f7e6
TC
2640 unless (defined wantarray) {
2641 my @caller = caller;
2642 warn "copy() called in void context - copy() returns the copied image at $caller[1] line $caller[2]\n";
2643 return;
2644 }
2645
faa9b3e7
TC
2646 if ($opts{matrix}) {
2647 my $xsize = $opts{xsize} || $self->getwidth;
2648 my $ysize = $opts{ysize} || $self->getheight;
142c26ff 2649
faa9b3e7 2650 my $result = Imager->new;
0d3b936e
TC
2651 if ($opts{back}) {
2652 $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
2653 $opts{matrix}, $opts{back})
2654 or return undef;
2655 }
2656 else {
2657 $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
2658 $opts{matrix})
2659 or return undef;
2660 }
faa9b3e7
TC
2661
2662 return $result;
2663 }
2664 else {
2665 $self->{ERRSTR} = "matrix parameter required";
2666 return undef;
2667 }
2668}
2669
2670# blame Leolo :)
2671*yatf = \&matrix_transform;
02d1d628
AMH
2672
2673# These two are supported for legacy code only
2674
2675sub i_color_new {
faa9b3e7 2676 return Imager::Color->new(@_);
02d1d628
AMH
2677}
2678
2679sub i_color_set {
faa9b3e7 2680 return Imager::Color::set(@_);
02d1d628
AMH
2681}
2682
02d1d628 2683# Draws a box between the specified corner points.
02d1d628
AMH
2684sub box {
2685 my $self=shift;
3b000586
TC
2686 my $raw = $self->{IMG};
2687
2688 unless ($raw) {
2689 $self->{ERRSTR}='empty input image';
2690 return undef;
2691 }
2692
2693 my %opts = @_;
02d1d628 2694
3b000586 2695 my ($xmin, $ymin, $xmax, $ymax);
02d1d628 2696 if (exists $opts{'box'}) {
3b000586
TC
2697 $xmin = _min($opts{'box'}->[0],$opts{'box'}->[2]);
2698 $xmax = _max($opts{'box'}->[0],$opts{'box'}->[2]);
2699 $ymin = _min($opts{'box'}->[1],$opts{'box'}->[3]);
2700 $ymax = _max($opts{'box'}->[1],$opts{'box'}->[3]);
2701 }
2702 else {
2703 defined($xmin = $opts{xmin}) or $xmin = 0;
2704 defined($xmax = $opts{xmax}) or $xmax = $self->getwidth()-1;
2705 defined($ymin = $opts{ymin}) or $ymin = 0;
2706 defined($ymax = $opts{ymax}) or $ymax = $self->getheight()-1;
02d1d628
AMH
2707 }
2708
f1ac5027 2709 if ($opts{filled}) {
4dd88895
TC
2710 my $color = $opts{'color'};
2711
2712 if (defined $color) {
813d4d0a 2713 unless (_is_color_object($color)) {
4dd88895
TC
2714 $color = _color($color);
2715 unless ($color) {
2716 $self->{ERRSTR} = $Imager::ERRSTR;
2717 return;
2718 }
2719 }
3a9a4241 2720 }
4dd88895
TC
2721 else {
2722 $color = i_color_new(255,255,255,255);
2723 }
2724
7477ff14
TC
2725 if ($color->isa("Imager::Color")) {
2726 i_box_filled($raw, $xmin, $ymin,$xmax, $ymax, $color);
2727 }
2728 else {
2729 i_box_filledf($raw, $xmin, $ymin,$xmax, $ymax, $color);
2730 }
f1ac5027
TC
2731 }
2732 elsif ($opts{fill}) {
2733 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2734 # assume it's a hash ref
2735 require 'Imager/Fill.pm';
141a6114
TC
2736 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2737 $self->{ERRSTR} = $Imager::ERRSTR;
2738 return undef;
2739 }
f1ac5027 2740 }
3b000586 2741 i_box_cfill($raw, $xmin, $ymin, $xmax, $ymax, $opts{fill}{fill});
f1ac5027 2742 }
cdd23610 2743 else {
4dd88895
TC
2744 my $color = $opts{'color'};
2745 if (defined $color) {
813d4d0a 2746 unless (_is_color_object($color)) {
4dd88895
TC
2747 $color = _color($color);
2748 unless ($color) {
2749 $self->{ERRSTR} = $Imager::ERRSTR;
2750 return;
2751 }
2752 }
2753 }
2754 else {
2755 $color = i_color_new(255, 255, 255, 255);
2756 }
3a9a4241 2757 unless ($color) {
cdd23610
AMH
2758 $self->{ERRSTR} = $Imager::ERRSTR;
2759 return;
3a9a4241 2760 }
3b000586 2761 i_box($raw, $xmin, $ymin, $xmax, $ymax, $color);
f1ac5027 2762 }
3b000586 2763
02d1d628
AMH
2764 return $self;
2765}
2766
02d1d628
AMH
2767sub arc {
2768 my $self=shift;
2769 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
40068b33
TC
2770 my $dflcl= [ 255, 255, 255, 255];
2771 my $good = 1;
2772 my %opts=
2773 (
2774 color=>$dflcl,
2775 'r'=>_min($self->getwidth(),$self->getheight())/3,
2776 'x'=>$self->getwidth()/2,
2777 'y'=>$self->getheight()/2,
2778 'd1'=>0, 'd2'=>361,
2779 filled => 1,
2780 @_,
2781 );
a8652edf
TC
2782 if ($opts{aa}) {
2783 if ($opts{fill}) {
2784 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2785 # assume it's a hash ref
2786 require 'Imager/Fill.pm';
2787 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2788 $self->{ERRSTR} = $Imager::ERRSTR;
2789 return;
2790 }
2791 }
2792 i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
2793 $opts{'d2'}, $opts{fill}{fill});
2794 }
40068b33 2795 elsif ($opts{filled}) {
a8652edf
TC
2796 my $color = _color($opts{'color'});
2797 unless ($color) {
2798 $self->{ERRSTR} = $Imager::ERRSTR;
2799 return;
2800 }
2801 if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
2802 i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
2803 $color);
2804 }
2805 else {
2806 i_arc_aa($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
2807 $opts{'d1'}, $opts{'d2'}, $color);
569795e8 2808 }
f1ac5027 2809 }
40068b33
TC
2810 else {
2811 my $color = _color($opts{'color'});
2812 if ($opts{d2} - $opts{d1} >= 360) {
2813 $good = i_circle_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $color);
2814 }
2815 else {
2816 $good = i_arc_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $opts{'d1'}, $opts{'d2'}, $color);
2817 }
2818 }
f1ac5027
TC
2819 }
2820 else {
a8652edf
TC
2821 if ($opts{fill}) {
2822 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2823 # assume it's a hash ref
2824 require 'Imager/Fill.pm';
2825 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2826 $self->{ERRSTR} = $Imager::ERRSTR;
2827 return;
2828 }
2829 }
2830 i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
2831 $opts{'d2'}, $opts{fill}{fill});
0d321238
TC
2832 }
2833 else {
a8652edf
TC
2834 my $color = _color($opts{'color'});
2835 unless ($color) {
2836 $self->{ERRSTR} = $Imager::ERRSTR;
40068b33
TC
2837 return;
2838 }
2839 if ($opts{filled}) {
2840 i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
2841 $opts{'d1'}, $opts{'d2'}, $color);
2842 }
2843 else {
2844 if ($opts{d1} == 0 && $opts{d2} == 361) {
2845 $good = i_circle_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $color);
2846 }
2847 else {
2848 $good = i_arc_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $opts{d1}, $opts{d2}, $color);
2849 }
a8652edf 2850 }
0d321238 2851 }
f1ac5027 2852 }
40068b33
TC
2853 unless ($good) {
2854 $self->_set_error($self->_error_as_msg);
2855 return;
2856 }
f1ac5027 2857
02d1d628
AMH
2858 return $self;
2859}
2860
aa833c97
AMH
2861# Draws a line from one point to the other
2862# the endpoint is set if the endp parameter is set which it is by default.
2863# to turn of the endpoint being set use endp=>0 when calling line.
02d1d628
AMH
2864
2865sub line {
2866 my $self=shift;
2867 my $dflcl=i_color_new(0,0,0,0);
aa833c97
AMH
2868 my %opts=(color=>$dflcl,
2869 endp => 1,
2870 @_);
02d1d628
AMH
2871 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2872
2873 unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; }
2874 unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; }
2875
3a9a4241 2876 my $color = _color($opts{'color'});
aa833c97
AMH
2877 unless ($color) {
2878 $self->{ERRSTR} = $Imager::ERRSTR;
2879 return;
3a9a4241 2880 }
aa833c97 2881
3a9a4241 2882 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628 2883 if ($opts{antialias}) {
aa833c97 2884 i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
b437ce0a 2885 $color, $opts{endp});
02d1d628 2886 } else {
aa833c97
AMH
2887 i_line($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
2888 $color, $opts{endp});
02d1d628
AMH
2889 }
2890 return $self;
2891}
2892
2893# Draws a line between an ordered set of points - It more or less just transforms this
2894# into a list of lines.
2895
2896sub polyline {
2897 my $self=shift;
2898 my ($pt,$ls,@points);
2899 my $dflcl=i_color_new(0,0,0,0);
2900 my %opts=(color=>$dflcl,@_);
2901
2902 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2903
2904 if (exists($opts{points})) { @points=@{$opts{points}}; }
2905 if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) {
2906 @points=map { [ $opts{'x'}->[$_],$opts{'y'}->[$_] ] } (0..(scalar @{$opts{'x'}}-1));
2907 }
2908
2909# print Dumper(\@points);
2910
3a9a4241
TC
2911 my $color = _color($opts{'color'});
2912 unless ($color) {
2913 $self->{ERRSTR} = $Imager::ERRSTR;
2914 return;
2915 }
2916 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628
AMH
2917 if ($opts{antialias}) {
2918 for $pt(@points) {
3a9a4241 2919 if (defined($ls)) {
b437ce0a 2920 i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color, 1);
3a9a4241 2921 }
02d1d628
AMH
2922 $ls=$pt;
2923 }
2924 } else {
2925 for $pt(@points) {
3a9a4241 2926 if (defined($ls)) {
aa833c97 2927 i_line($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color,1);
3a9a4241 2928 }
02d1d628
AMH
2929 $ls=$pt;
2930 }
2931 }
2932 return $self;
2933}
2934
d0e7bfee
AMH
2935sub polygon {
2936 my $self = shift;
2937 my ($pt,$ls,@points);
2938 my $dflcl = i_color_new(0,0,0,0);
2939 my %opts = (color=>$dflcl, @_);
2940
2941 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2942
2943 if (exists($opts{points})) {
2944 $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ];
2945 $opts{'y'} = [ map { $_->[1] } @{$opts{points}} ];
2946 }
2947
2948 if (!exists $opts{'x'} or !exists $opts{'y'}) {
2949 $self->{ERRSTR} = 'no points array, or x and y arrays.'; return undef;
2950 }
2951
43c5dacb
TC
2952 if ($opts{'fill'}) {
2953 unless (UNIVERSAL::isa($opts{'fill'}, 'Imager::Fill')) {
2954 # assume it's a hash ref
2955 require 'Imager/Fill.pm';
2956 unless ($opts{'fill'} = Imager::Fill->new(%{$opts{'fill'}})) {
2957 $self->{ERRSTR} = $Imager::ERRSTR;
2958 return undef;
2959 }
2960 }
2961 i_poly_aa_cfill($self->{IMG}, $opts{'x'}, $opts{'y'},
2962 $opts{'fill'}{'fill'});
2963 }
2964 else {
3a9a4241
TC
2965 my $color = _color($opts{'color'});
2966 unless ($color) {
2967 $self->{ERRSTR} = $Imager::ERRSTR;
2968 return;
2969 }
2970 i_poly_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
43c5dacb
TC
2971 }
2972
d0e7bfee
AMH
2973 return $self;
2974}
2975
2976
2977# this the multipoint bezier curve
02d1d628
AMH
2978# this is here more for testing that actual usage since
2979# this is not a good algorithm. Usually the curve would be
2980# broken into smaller segments and each done individually.
2981
2982sub polybezier {
2983 my $self=shift;
2984 my ($pt,$ls,@points);
2985 my $dflcl=i_color_new(0,0,0,0);
2986 my %opts=(color=>$dflcl,@_);
2987
2988 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2989
2990 if (exists $opts{points}) {
2991 $opts{'x'}=map { $_->[0]; } @{$opts{'points'}};
2992 $opts{'y'}=map { $_->[1]; } @{$opts{'points'}};
2993 }
2994
2995 unless ( @{$opts{'x'}} and @{$opts{'x'}} == @{$opts{'y'}} ) {
2996 $self->{ERRSTR}='Missing or invalid points.';
2997 return;
2998 }
2999
3a9a4241
TC
3000 my $color = _color($opts{'color'});
3001 unless ($color) {
3002 $self->{ERRSTR} = $Imager::ERRSTR;
3003 return;
3004 }
3005 i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$color);
02d1d628
AMH
3006 return $self;
3007}
3008
cc6483e0
TC
3009sub flood_fill {
3010 my $self = shift;
3011 my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
aa833c97
AMH
3012 my $rc;
3013
9d540150 3014 unless (exists $opts{'x'} && exists $opts{'y'}) {
cc6483e0
TC
3015 $self->{ERRSTR} = "missing seed x and y parameters";
3016 return undef;
3017 }
07d70837 3018
3efb0915
TC
3019 if ($opts{border}) {
3020 my $border = _color($opts{border});
3021 unless ($border) {
3022 $self->_set_error($Imager::ERRSTR);
3023 return;
3024 }
3025 if ($opts{fill}) {
3026 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
3027 # assume it's a hash ref
3028 require Imager::Fill;
3029 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
3030 $self->{ERRSTR} = $Imager::ERRSTR;
3031 return;
3032 }
569795e8 3033 }
3efb0915
TC
3034 $rc = i_flood_cfill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
3035 $opts{fill}{fill}, $border);
3036 }
3037 else {
3038 my $color = _color($opts{'color'});
3039 unless ($color) {
3040 $self->{ERRSTR} = $Imager::ERRSTR;
3041 return;
3042 }
3043 $rc = i_flood_fill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
3044 $color, $border);
3045 }
3046 if ($rc) {
3047 return $self;
3048 }
3049 else {
3050 $self->{ERRSTR} = $self->_error_as_msg();
3051 return;
cc6483e0 3052 }
cc6483e0
TC
3053 }
3054 else {
3efb0915
TC
3055 if ($opts{fill}) {
3056 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
3057 # assume it's a hash ref
3058 require 'Imager/Fill.pm';
3059 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
3060 $self->{ERRSTR} = $Imager::ERRSTR;
3061 return;
3062 }
3063 }
3064 $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
3065 }
3066 else {
3067 my $color = _color($opts{'color'});
3068 unless ($color) {
3069 $self->{ERRSTR} = $Imager::ERRSTR;
3070 return;
3071 }
3072 $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
3073 }
3074 if ($rc) {
3075 return $self;
3076 }
3077 else {
3078 $self->{ERRSTR} = $self->_error_as_msg();
aa833c97 3079 return;
3a9a4241 3080 }
3efb0915 3081 }
cc6483e0
TC
3082}
3083
591b5954 3084sub setpixel {
b3cdc973 3085 my ($self, %opts) = @_;
591b5954 3086
b3cdc973
TC
3087 my $color = $opts{color};
3088 unless (defined $color) {
3089 $color = $self->{fg};
3090 defined $color or $color = NC(255, 255, 255);
3091 }
3092
3093 unless (ref $color && UNIVERSAL::isa($color, "Imager::Color")) {
3094 $color = _color($color)
3095 or return undef;
3096 }
591b5954
TC
3097
3098 unless (exists $opts{'x'} && exists $opts{'y'}) {
3099 $self->{ERRSTR} = 'missing x and y parameters';
3100 return undef;
3101 }
3102
3103 my $x = $opts{'x'};
3104 my $y = $opts{'y'};
591b5954
TC
3105 if (ref $x && ref $y) {
3106 unless (@$x == @$y) {
9650c424 3107 $self->{ERRSTR} = 'length of x and y mismatch';
837a4b43 3108 return;
591b5954 3109 }
837a4b43 3110 my $set = 0;
591b5954
TC
3111 if ($color->isa('Imager::Color')) {
3112 for my $i (0..$#{$opts{'x'}}) {
837a4b43
TC
3113 i_ppix($self->{IMG}, $x->[$i], $y->[$i], $color)
3114 or ++$set;
591b5954
TC
3115 }
3116 }
3117 else {
3118 for my $i (0..$#{$opts{'x'}}) {
837a4b43
TC
3119 i_ppixf($self->{IMG}, $x->[$i], $y->[$i], $color)
3120 or ++$set;
591b5954
TC
3121 }
3122 }
837a4b43
TC
3123 $set or return;
3124 return $set;
591b5954
TC
3125 }
3126 else {
3127 if ($color->isa('Imager::Color')) {
837a4b43
TC
3128 i_ppix($self->{IMG}, $x, $y, $color)
3129 and return;
591b5954
TC
3130 }
3131 else {
837a4b43
TC
3132 i_ppixf($self->{IMG}, $x, $y, $color)
3133 and return;
591b5954
TC
3134 }
3135 }
3136
3137 $self;
3138}
3139
3140sub getpixel {
3141 my $self = shift;
3142
a9fa203f 3143 my %opts = ( "type"=>'8bit', @_);
591b5954
TC
3144
3145 unless (exists $opts{'x'} && exists $opts{'y'}) {
3146 $self->{ERRSTR} = 'missing x and y parameters';
3147 return undef;
3148 }
3149
3150 my $x = $opts{'x'};
3151 my $y = $opts{'y'};
3152 if (ref $x && ref $y) {
3153 unless (@$x == @$y) {
3154 $self->{ERRSTR} = 'length of x and y mismatch';
3155 return undef;
3156 }
3157 my @result;
a9fa203f 3158 if ($opts{"type"} eq '8bit') {
591b5954
TC
3159 for my $i (0..$#{$opts{'x'}}) {
3160 push(@result, i_get_pixel($self->{IMG}, $x->[$i], $y->[$i]));
3161 }
3162 }
3163 else {
3164 for my $i (0..$#{$opts{'x'}}) {
3165 push(@result, i_gpixf($self->{IMG}, $x->[$i], $y->[$i]));
3166 }
3167 }
3168 return wantarray ? @result : \@result;
3169 }
3170 else {
a9fa203f 3171 if ($opts{"type"} eq '8bit') {
591b5954
TC
3172 return i_get_pixel($self->{IMG}, $x, $y);
3173 }
3174 else {
3175 return i_gpixf($self->{IMG}, $x, $y);
3176 }
3177 }
3178
3179 $self;
3180}
3181
ca4d914e
TC
3182sub getscanline {
3183 my $self = shift;
3184 my %opts = ( type => '8bit', x=>0, @_);
3185
4cda4e76
TC
3186 $self->_valid_image or return;
3187
ca4d914e
TC
3188 defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
3189
3190 unless (defined $opts{'y'}) {
3191 $self->_set_error("missing y parameter");
3192 return;
3193 }
3194
3195 if ($opts{type} eq '8bit') {
3196 return i_glin($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
4cda4e76 3197 $opts{'y'});
ca4d914e
TC
3198 }
3199 elsif ($opts{type} eq 'float') {
3200 return i_glinf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
4cda4e76
TC
3201 $opts{'y'});
3202 }
3203 elsif ($opts{type} eq 'index') {
3204 unless (i_img_type($self->{IMG})) {
3205 $self->_set_error("type => index only valid on paletted images");
3206 return;
3207 }
3208 return i_gpal($self->{IMG}, $opts{x}, $opts{x} + $opts{width},
3209 $opts{'y'});
ca4d914e
TC
3210 }
3211 else {
3212 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3213 return;
3214 }
3215}
3216
3217sub setscanline {
3218 my $self = shift;
3219 my %opts = ( x=>0, @_);
3220
4cda4e76
TC
3221 $self->_valid_image or return;
3222
ca4d914e
TC
3223 unless (defined $opts{'y'}) {
3224 $self->_set_error("missing y parameter");
3225 return;
3226 }
3227
3228 if (!$opts{type}) {
3229 if (ref $opts{pixels} && @{$opts{pixels}}) {
3230 # try to guess the type
3231 if ($opts{pixels}[0]->isa('Imager::Color')) {
3232 $opts{type} = '8bit';
3233 }
3234 elsif ($opts{pixels}[0]->isa('Imager::Color::Float')) {
3235 $opts{type} = 'float';
3236 }
3237 else {
3238 $self->_set_error("missing type parameter and could not guess from pixels");
3239 return;
3240 }
3241 }
3242 else {
3243 # default
3244 $opts{type} = '8bit';
3245 }
3246 }
3247
3248 if ($opts{type} eq '8bit') {
3249 if (ref $opts{pixels}) {
3250 return i_plin($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3251 }
3252 else {
3253 return i_plin($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3254 }
3255 }
3256 elsif ($opts{type} eq 'float') {
3257 if (ref $opts{pixels}) {
3258 return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3259 }
3260 else {
3261 return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3262 }
3263 }
4cda4e76
TC
3264 elsif ($opts{type} eq 'index') {
3265 if (ref $opts{pixels}) {
3266 return i_ppal($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3267 }
3268 else {
3269 return i_ppal_p($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3270 }
3271 }
ca4d914e
TC
3272 else {
3273 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3274 return;
3275 }
3276}
3277
3278sub getsamples {
3279 my $self = shift;
bd8052a6 3280 my %opts = ( type => '8bit', x=>0, offset => 0, @_);
ca4d914e
TC
3281
3282 defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
3283
3284 unless (defined $opts{'y'}) {
3285 $self->_set_error("missing y parameter");
3286 return;
3287 }
3288
3289 unless ($opts{channels}) {
3290 $opts{channels} = [ 0 .. $self->getchannels()-1 ];
3291 }
3292
bd8052a6
TC
3293 if ($opts{target}) {
3294 my $target = $opts{target};
3295 my $offset = $opts{offset};
3296 if ($opts{type} eq '8bit') {
3297 my @samples = i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3298 $opts{y}, @{$opts{channels}})
3299 or return;
3300 @{$target}{$offset .. $offset + @samples - 1} = @samples;
3301 return scalar(@samples);
3302 }
3303 elsif ($opts{type} eq 'float') {
3304 my @samples = i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3305 $opts{y}, @{$opts{channels}});
3306 @{$target}{$offset .. $offset + @samples - 1} = @samples;
3307 return scalar(@samples);
3308 }
3309 elsif ($opts{type} =~ /^(\d+)bit$/) {
3310 my $bits = $1;
3311
3312 my @data;
3313 my $count = i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3314 $opts{y}, $bits, $target,
3315 $offset, @{$opts{channels}});
3316 unless (defined $count) {
3317 $self->_set_error(Imager->_error_as_msg);
3318 return;
3319 }
3320
3321 return $count;
3322 }
3323 else {
3324 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3325 return;
3326 }
ca4d914e
TC
3327 }
3328 else {
bd8052a6
TC
3329 if ($opts{type} eq '8bit') {
3330 return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3331 $opts{y}, @{$opts{channels}});
3332 }
3333 elsif ($opts{type} eq 'float') {
3334 return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3335 $opts{y}, @{$opts{channels}});
3336 }
3337 elsif ($opts{type} =~ /^(\d+)bit$/) {
3338 my $bits = $1;
3339
3340 my @data;
3341 i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3342 $opts{y}, $bits, \@data, 0, @{$opts{channels}})
3343 or return;
3344 return @data;
3345 }
3346 else {
3347 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3348 return;
3349 }
3350 }
3351}
3352
3353sub setsamples {
3354 my $self = shift;
3355 my %opts = ( x => 0, offset => 0, @_ );
3356
3357 unless ($self->{IMG}) {
3358 $self->_set_error('setsamples: empty input image');
3359 return;
3360 }
3361
3362 unless(defined $opts{data} && ref $opts{data}) {
3363 $self->_set_error('setsamples: data parameter missing or invalid');
ca4d914e
TC
3364 return;
3365 }
bd8052a6
TC
3366
3367 unless ($opts{channels}) {
3368 $opts{channels} = [ 0 .. $self->getchannels()-1 ];
3369 }
3370
3371 unless ($opts{type} && $opts{type} =~ /^(\d+)bit$/) {
3372 $self->_set_error('setsamples: type parameter missing or invalid');
3373 return;
3374 }
3375 my $bits = $1;
3376
3377 unless (defined $opts{width}) {
3378 $opts{width} = $self->getwidth() - $opts{x};
3379 }
3380
3381 my $count = i_psamp_bits($self->{IMG}, $opts{x}, $opts{y}, $bits,
3382 $opts{channels}, $opts{data}, $opts{offset},
3383 $opts{width});
3384 unless (defined $count) {
3385 $self->_set_error(Imager->_error_as_msg);
3386 return;
3387 }
3388
3389 return $count;
ca4d914e
TC
3390}
3391
f5991c03
TC
3392# make an identity matrix of the given size
3393sub _identity {
3394 my ($size) = @_;
3395
3396 my $matrix = [ map { [ (0) x $size ] } 1..$size ];
3397 for my $c (0 .. ($size-1)) {
3398 $matrix->[$c][$c] = 1;
3399 }
3400 return $matrix;
3401}
3402
3403# general function to convert an image
3404sub convert {
3405 my ($self, %opts) = @_;
3406 my $matrix;
3407
34b3f7e6
TC
3408 unless (defined wantarray) {
3409 my @caller = caller;
3410 warn "convert() called in void context - convert() returns the converted image at $caller[1] line $caller[2]\n";
3411 return;
3412 }
3413
f5991c03
TC
3414 # the user can either specify a matrix or preset
3415 # the matrix overrides the preset
3416 if (!exists($opts{matrix})) {
3417 unless (exists($opts{preset})) {
3418 $self->{ERRSTR} = "convert() needs a matrix or preset";
3419 return;
3420 }
3421 else {
3422 if ($opts{preset} eq 'gray' || $opts{preset} eq 'grey') {
3423 # convert to greyscale, keeping the alpha channel if any
3424 if ($self->getchannels == 3) {
3425 $matrix = [ [ 0.222, 0.707, 0.071 ] ];
3426 }
3427 elsif ($self->getchannels == 4) {
3428 # preserve the alpha channel
3429 $matrix = [ [ 0.222, 0.707, 0.071, 0 ],
3430 [ 0, 0, 0, 1 ] ];
3431 }
3432 else {
3433 # an identity
3434 $matrix = _identity($self->getchannels);
3435 }
3436 }
3437 elsif ($opts{preset} eq 'noalpha') {
3438 # strip the alpha channel
3439 if ($self->getchannels == 2 or $self->getchannels == 4) {
3440 $matrix = _identity($self->getchannels);
3441 pop(@$matrix); # lose the alpha entry
3442 }
3443 else {
3444 $matrix = _identity($self->getchannels);
3445 }
3446 }
3447 elsif ($opts{preset} eq 'red' || $opts{preset} eq 'channel0') {
3448 # extract channel 0
3449 $matrix = [ [ 1 ] ];
3450 }
3451 elsif ($opts{preset} eq 'green' || $opts{preset} eq 'channel1') {
3452 $matrix = [ [ 0, 1 ] ];
3453 }
3454 elsif ($opts{preset} eq 'blue' || $opts{preset} eq 'channel2') {
3455 $matrix = [ [ 0, 0, 1 ] ];
3456 }
3457 elsif ($opts{preset} eq 'alpha') {
3458 if ($self->getchannels == 2 or $self->getchannels == 4) {
3459 $matrix = [ [ (0) x ($self->getchannels-1), 1 ] ];
3460 }
3461 else {
3462 # the alpha is just 1 <shrug>
3463 $matrix = [ [ (0) x $self->getchannels, 1 ] ];
3464 }
3465 }
3466 elsif ($opts{preset} eq 'rgb') {
3467 if ($self->getchannels == 1) {
3468 $matrix = [ [ 1 ], [ 1 ], [ 1 ] ];
3469 }
3470 elsif ($self->getchannels == 2) {
3471 # preserve the alpha channel
3472 $matrix = [ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ], [ 0, 1 ] ];
3473 }
3474 else {
3475 $matrix = _identity($self->getchannels);
3476 }
3477 }
3478 elsif ($opts{preset} eq 'addalpha') {
3479 if ($self->getchannels == 1) {
3480 $matrix = _identity(2);
3481 }
3482 elsif ($self->getchannels == 3) {
3483 $matrix = _identity(4);
3484 }
3485 else {
3486 $matrix = _identity($self->getchannels);
3487 }
3488 }
3489 else {
3490 $self->{ERRSTR} = "Unknown convert preset $opts{preset}";
3491 return undef;
3492 }
3493 }
3494 }
3495 else {
3496 $matrix = $opts{matrix};
3497 }
3498
d5477d3d
TC
3499 my $new = Imager->new;
3500 $new->{IMG} = i_convert($self->{IMG}, $matrix);
3501 unless ($new->{IMG}) {
f5991c03
TC
3502 # most likely a bad matrix
3503 $self->{ERRSTR} = _error_as_msg();
3504 return undef;
3505 }
3506 return $new;
3507}
40eba1ea 3508
b47464c1
TC
3509# combine channels from multiple input images, a class method
3510sub combine {
3511 my ($class, %opts) = @_;
3512
3513 my $src = delete $opts{src};
3514 unless ($src) {
3515 $class->_set_error("src parameter missing");
3516 return;
3517 }
3518 my @imgs;
3519 my $index = 0;
3520 for my $img (@$src) {
3521 unless (eval { $img->isa("Imager") }) {
3522 $class->_set_error("src must contain image objects");
3523 return;
3524 }
3525 unless ($img->{IMG}) {
3526 $class->_set_error("empty input image");
3527 return;
3528 }
3529 push @imgs, $img->{IMG};
3530 }
3531 my $result;
3532 if (my $channels = delete $opts{channels}) {
3533 $result = i_combine(\@imgs, $channels);
3534 }
3535 else {
3536 $result = i_combine(\@imgs);
3537 }
3538 unless ($result) {
3539 $class->_set_error($class->_error_as_msg);
3540 return;
3541 }
3542
3543 my $img = $class->new;
3544 $img->{IMG} = $result;
3545
3546 return $img;
3547}
3548
40eba1ea 3549
40eba1ea 3550# general function to map an image through lookup tables
9495ee93 3551
40eba1ea
AMH
3552sub map {
3553 my ($self, %opts) = @_;
9495ee93 3554 my @chlist = qw( red green blue alpha );
40eba1ea
AMH
3555
3556 if (!exists($opts{'maps'})) {
3557 # make maps from channel maps
3558 my $chnum;
3559 for $chnum (0..$#chlist) {
9495ee93
AMH
3560 if (exists $opts{$chlist[$chnum]}) {
3561 $opts{'maps'}[$chnum] = $opts{$chlist[$chnum]};
3562 } elsif (exists $opts{'all'}) {
3563 $opts{'maps'}[$chnum] = $opts{'all'};
3564 }
40eba1ea
AMH
3565 }
3566 }
3567 if ($opts{'maps'} and $self->{IMG}) {
3568 i_map($self->{IMG}, $opts{'maps'} );
3569 }
3570 return $self;
3571}
3572
dff75dee
TC
3573sub difference {
3574 my ($self, %opts) = @_;
3575
3576 defined $opts{mindist} or $opts{mindist} = 0;
3577
3578 defined $opts{other}
3579 or return $self->_set_error("No 'other' parameter supplied");
3580 defined $opts{other}{IMG}
3581 or return $self->_set_error("No image data in 'other' image");
3582
3583 $self->{IMG}
3584 or return $self->_set_error("No image data");
3585
3586 my $result = Imager->new;
3587 $result->{IMG} = i_diff_image($self->{IMG}, $opts{other}{IMG},
3588 $opts{mindist})
3589 or return $self->_set_error($self->_error_as_msg());
3590
3591 return $result;
3592}
3593
02d1d628
AMH
3594# destructive border - image is shrunk by one pixel all around
3595
3596sub border {
3597 my ($self,%opts)=@_;
3598 my($tx,$ty)=($self->getwidth()-1,$self->getheight()-1);
3599 $self->polyline('x'=>[0,$tx,$tx,0,0],'y'=>[0,0,$ty,$ty,0],%opts);
3600}
3601
3602
3603# Get the width of an image
3604
3605sub getwidth {
3606 my $self = shift;
3b000586
TC
3607
3608 if (my $raw = $self->{IMG}) {
3609 return i_img_get_width($raw);
3610 }
3611 else {
3612 $self->{ERRSTR} = 'image is empty'; return undef;
3613 }
02d1d628
AMH
3614}
3615
3616# Get the height of an image
3617
3618sub getheight {
3619 my $self = shift;
3b000586
TC
3620
3621 if (my $raw = $self->{IMG}) {
3622 return i_img_get_height($raw);
3623 }
3624 else {
3625 $self->{ERRSTR} = 'image is empty'; return undef;
3626 }
02d1d628
AMH
3627}
3628
3629# Get number of channels in an image
3630
3631sub getchannels {
3632 my $self = shift;
3633 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
3634 return i_img_getchannels($self->{IMG});
3635}
3636
3637# Get channel mask
3638
3639sub getmask {
3640 my $self = shift;
3641 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
3642 return i_img_getmask($self->{IMG});
3643}
3644
3645# Set channel mask
3646
3647sub setmask {
3648 my $self = shift;
3649 my %opts = @_;
35f40526
TC
3650 if (!defined($self->{IMG})) {
3651 $self->{ERRSTR} = 'image is empty';
3652 return undef;
3653 }
3654 unless (defined $opts{mask}) {
3655 $self->_set_error("mask parameter required");
3656 return;
3657 }
02d1d628 3658 i_img_setmask( $self->{IMG} , $opts{mask} );
35f40526
TC
3659
3660 1;
02d1d628
AMH
3661}
3662
3663# Get number of colors in an image
3664
3665sub getcolorcount {
3666 my $self=shift;
9d540150 3667 my %opts=('maxcolors'=>2**30,@_);
02d1d628
AMH
3668 if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
3669 my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
3670 return ($rc==-1? undef : $rc);
3671}
3672
fe622da1
TC
3673# Returns a reference to a hash. The keys are colour named (packed) and the
3674# values are the number of pixels in this colour.
3675sub getcolorusagehash {
a60905e4
TC
3676 my $self = shift;
3677
3678 my %opts = ( maxcolors => 2**30, @_ );
3679 my $max_colors = $opts{maxcolors};
3680 unless (defined $max_colors && $max_colors > 0) {
3681 $self->_set_error('maxcolors must be a positive integer');
3682 return;
3683 }
3684
3685 unless (defined $self->{IMG}) {
3686 $self->_set_error('empty input image');
3687 return;
3688 }
3689
3690 my $channels= $self->getchannels;
3691 # We don't want to look at the alpha channel, because some gifs using it
3692 # doesn't define it for every colour (but only for some)
3693 $channels -= 1 if $channels == 2 or $channels == 4;
3694 my %color_use;
3695 my $height = $self->getheight;
3696 for my $y (0 .. $height - 1) {
3697 my $colors = $self->getsamples('y' => $y, channels => [ 0 .. $channels - 1 ]);
3698 while (length $colors) {
3699 $color_use{ substr($colors, 0, $channels, '') }++;
fe622da1 3700 }
a60905e4
TC
3701 keys %color_use > $max_colors
3702 and return;
3703 }
3704 return \%color_use;
fe622da1
TC
3705}
3706
3707# This will return a ordered array of the colour usage. Kind of the sorted
3708# version of the values of the hash returned by getcolorusagehash.
3709# You might want to add safety checks and change the names, etc...
3710sub getcolorusage {
a60905e4
TC
3711 my $self = shift;
3712
3713 my %opts = ( maxcolors => 2**30, @_ );
3714 my $max_colors = $opts{maxcolors};
3715 unless (defined $max_colors && $max_colors > 0) {
3716 $self->_set_error('maxcolors must be a positive integer');
3717 return;
3718 }
3719
3720 unless (defined $self->{IMG}) {
3721 $self->_set_error('empty input image');
3722 return undef;
3723 }
3724
3725 return i_get_anonymous_color_histo($self->{IMG}, $max_colors);
fe622da1
TC
3726}
3727
02d1d628
AMH
3728# draw string to an image
3729
3730sub string {
3731 my $self = shift;
3732 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
3733
3734 my %input=('x'=>0, 'y'=>0, @_);
4314a320 3735 defined($input{string}) or $input{string} = $input{text};
02d1d628 3736
e922ae66 3737 unless(defined $input{string}) {
02d1d628
AMH
3738 $self->{ERRSTR}="missing required parameter 'string'";
3739 return;
3740 }
3741
3742 unless($input{font}) {
3743 $self->{ERRSTR}="missing required parameter 'font'";
3744 return;
3745 }
3746
faa9b3e7 3747 unless ($input{font}->draw(image=>$self, %input)) {
faa9b3e7
TC
3748 return;
3749 }
02d1d628
AMH
3750
3751 return $self;
3752}
3753
a7ccc5e2
TC
3754sub align_string {
3755 my $self = shift;
e922ae66
TC
3756
3757 my $img;
3758 if (ref $self) {
3759 unless ($self->{IMG}) {
3760 $self->{ERRSTR}='empty input image';
3761 return;
3762 }
c9cb3397 3763 $img = $self;
e922ae66
TC
3764 }
3765 else {
3766 $img = undef;
3767 }
a7ccc5e2
TC
3768
3769 my %input=('x'=>0, 'y'=>0, @_);
120f4287
TC
3770 defined $input{string}
3771 or $input{string} = $input{text};
a7ccc5e2
TC
3772
3773 unless(exists $input{string}) {
e922ae66 3774 $self->_set_error("missing required parameter 'string'");
a7ccc5e2
TC
3775 return;
3776 }
3777
3778 unless($input{font}) {
e922ae66 3779 $self->_set_error("missing required parameter 'font'");
a7ccc5e2
TC
3780 return;
3781 }
3782
3783 my @result;
e922ae66 3784 unless (@result = $input{font}->align(image=>$img, %input)) {
a7ccc5e2
TC
3785 return;
3786 }
3787
3788 return wantarray ? @result : $result[0];
3789}
3790
77157728
TC
3791my @file_limit_names = qw/width height bytes/;
3792
3793sub set_file_limits {
3794 shift;
3795
3796 my %opts = @_;
3797 my %values;
3798
3799 if ($opts{reset}) {
3800 @values{@file_limit_names} = (0) x @file_limit_names;
3801 }
3802 else {
3803 @values{@file_limit_names} = i_get_image_file_limits();
3804 }
3805
3806 for my $key (keys %values) {
3807 defined $opts{$key} and $values{$key} = $opts{$key};
3808 }
3809
3810 i_set_image_file_limits($values{width}, $values{height}, $values{bytes});
3811}
3812
3813sub get_file_limits {
3814 i_get_image_file_limits();
3815}
3816
02d1d628
AMH
3817# Shortcuts that can be exported
3818
3819sub newcolor { Imager::Color->new(@_); }
3820sub newfont { Imager::Font->new(@_); }
4697b0b9
TC
3821sub NCF {
3822 require Imager::Color::Float;
3823 return Imager::Color::Float->new(@_);
3824}
02d1d628
AMH
3825
3826*NC=*newcolour=*newcolor;
3827*NF=*newfont;
3828
3829*open=\&read;
3830*circle=\&arc;
3831
3832
3833#### Utility routines
3834
faa9b3e7
TC
3835sub errstr {
3836 ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
3837}
02d1d628 3838
10461f9a
TC
3839sub _set_error {
3840 my ($self, $msg) = @_;
3841
3842 if (ref $self) {
3843 $self->{ERRSTR} = $msg;
3844 }
3845 else {
3846 $ERRSTR = $msg;
3847 }
dff75dee 3848 return;
10461f9a
TC
3849}
3850
02d1d628
AMH
3851# Default guess for the type of an image from extension
3852
3853sub def_guess_type {
3854 my $name=lc(shift);
3855 my $ext;
3856 $ext=($name =~ m/\.([^\.]+)$/)[0];
3857 return 'tiff' if ($ext =~ m/^tiff?$/);
3858 return 'jpeg' if ($ext =~ m/^jpe?g$/);
3859 return 'pnm' if ($ext =~ m/^p[pgb]m$/);
3860 return 'png' if ($ext eq "png");
705fd961 3861 return 'bmp' if ($ext eq "bmp" || $ext eq "dib");
1ec86afa 3862 return 'tga' if ($ext eq "tga");
d5477d3d 3863 return 'sgi' if ($ext eq "rgb" || $ext eq "bw" || $ext eq "sgi" || $ext eq "rgba");
02d1d628 3864 return 'gif' if ($ext eq "gif");
10461f9a 3865 return 'raw' if ($ext eq "raw");
2b405c9e 3866 return lc $ext; # best guess
02d1d628
AMH
3867 return ();
3868}
3869
9b1ec2b8
TC
3870sub combines {
3871 return @combine_types;
3872}
3873
02d1d628
AMH
3874# get the minimum of a list
3875
bf1573f9 3876sub _min {
02d1d628
AMH
3877 my $mx=shift;
3878 for(@_) { if ($_<$mx) { $mx=$_; }}
3879 return $mx;
3880}
3881
3882# get the maximum of a list
3883
bf1573f9 3884sub _max {
02d1d628
AMH
3885 my $mx=shift;
3886 for(@_) { if ($_>$mx) { $mx=$_; }}
3887 return $mx;
3888}
3889
3890# string stuff for iptc headers
3891
bf1573f9 3892sub _clean {
02d1d628
AMH
3893 my($str)=$_[0];
3894 $str = substr($str,3);
3895 $str =~ s/[\n\r]//g;
3896 $str =~ s/\s+/ /g;
3897 $str =~ s/^\s//;
3898 $str =~ s/\s$//;
3899 return $str;
3900}
3901
3902# A little hack to parse iptc headers.
3903
3904sub parseiptc {
3905 my $self=shift;
3906 my(@sar,$item,@ar);
3907 my($caption,$photogr,$headln,$credit);
3908
3909 my $str=$self->{IPTCRAW};
3910
24ae6325
TC
3911 defined $str
3912 or return;
02d1d628
AMH
3913
3914 @ar=split(/8BIM/,$str);
3915
3916 my $i=0;
3917 foreach (@ar) {
3918 if (/^\004\004/) {
3919 @sar=split(/\034\002/);
3920 foreach $item (@sar) {
cdd23610 3921 if ($item =~ m/^x/) {
bf1573f9 3922 $caption = _clean($item);
02d1d628
AMH
3923 $i++;
3924 }
cdd23610 3925 if ($item =~ m/^P/) {
bf1573f9 3926 $photogr = _clean($item);
02d1d628
AMH
3927 $i++;
3928 }
cdd23610 3929 if ($item =~ m/^i/) {
bf1573f9 3930 $headln = _clean($item);
02d1d628
AMH
3931 $i++;
3932 }
cdd23610 3933 if ($item =~ m/^n/) {
bf1573f9 3934 $credit = _clean($item);
02d1d628
AMH
3935 $i++;
3936 }
3937 }
3938 }
3939 }
3940 return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
3941}
3942
92bda632
TC
3943sub Inline {
3944 my ($lang) = @_;
3945
3946 $lang eq 'C'
3947 or die "Only C language supported";
3948
3949 require Imager::ExtUtils;
3950 return Imager::ExtUtils->inline_config;
3951}
02d1d628 3952
ffddd407
TC
3953# threads shouldn't try to close raw Imager objects
3954sub Imager::ImgRaw::CLONE_SKIP { 1 }
3955
2c331f9f
TC
3956sub preload {
3957 # this serves two purposes:
3958 # - a class method to load the file support modules included with Image
3959 # (or were included, once the library dependent modules are split out)
3960 # - something for Module::ScanDeps to analyze
3961 # https://rt.cpan.org/Ticket/Display.html?id=6566
3962 local $@;
3963 eval { require Imager::File::GIF };
3964 eval { require Imager::File::JPEG };
3965 eval { require Imager::File::PNG };
3966 eval { require Imager::File::SGI };
3967 eval { require Imager::File::TIFF };
3968 eval { require Imager::File::ICO };
3969 eval { require Imager::Font::W32 };
3970 eval { require Imager::Font::FT2 };
3971 eval { require Imager::Font::T1 };
3972}
3973
1d7e3124
TC
3974# backward compatibility for %formats
3975package Imager::FORMATS;
3976use strict;
3977use constant IX_FORMATS => 0;
3978use constant IX_LIST => 1;
3979use constant IX_INDEX => 2;
3980use constant IX_CLASSES => 3;
3981
3982sub TIEHASH {
3983 my ($class, $formats, $classes) = @_;
3984
3985 return bless [ $formats, [ ], 0, $classes ], $class;
3986}
3987
3988sub _check {
3989 my ($self, $key) = @_;
3990
3991 (my $file = $self->[IX_CLASSES]{$key} . ".pm") =~ s(::)(/)g;
3992 my $value;
3993 if (eval { require $file; 1 }) {
3994 $value = 1;
3995 }
3996 else {
3997 $value = undef;
3998 }
3999 $self->[IX_FORMATS]{$key} = $value;
4000
4001 return $value;
4002}
4003
4004sub FETCH {
4005 my ($self, $key) = @_;
4006
4007 exists $self->[IX_FORMATS]{$key} and return $self->[IX_FORMATS]{$key};
4008
4009 $self->[IX_CLASSES]{$key} or return undef;
4010
4011 return $self->_check($key);
4012}
4013
4014sub STORE {
4015 die "%Imager::formats is not user monifiable";
4016}
4017
4018sub DELETE {
4019 die "%Imager::formats is not user monifiable";
4020}
4021
4022sub CLEAR {
4023 die "%Imager::formats is not user monifiable";
4024}
4025
4026sub EXISTS {
4027 my ($self, $key) = @_;
4028
4029 if (exists $self->[IX_FORMATS]{$key}) {
4030 my $value = $self->[IX_FORMATS]{$key}
4031 or return;
4032 return 1;
4033 }
4034
4035 $self->_check($key) or return 1==0;
4036
4037 return 1==1;
4038}
4039
4040sub FIRSTKEY {
4041 my ($self) = @_;
4042
4043 unless (@{$self->[IX_LIST]}) {
4044 # full populate it
d7e4ec85
TC
4045 @{$self->[IX_LIST]} = grep $self->[IX_FORMATS]{$_},
4046 keys %{$self->[IX_FORMATS]};
1d7e3124
TC
4047
4048 for my $key (keys %{$self->[IX_CLASSES]}) {
4049 $self->[IX_FORMATS]{$key} and next;
4050 $self->_check($key)
4051 and push @{$self->[IX_LIST]}, $key;
4052 }
4053 }
4054
4055 @{$self->[IX_LIST]} or return;
4056 $self->[IX_INDEX] = 1;
4057 return $self->[IX_LIST][0];
4058}
4059
4060sub NEXTKEY {
4061 my ($self) = @_;
4062
4063 $self->[IX_INDEX] < @{$self->[IX_LIST]}
4064 or return;
4065
4066 return $self->[IX_LIST][$self->[IX_INDEX]++];
4067}
4068
4069sub SCALAR {
4070 my ($self) = @_;
4071
4072 return scalar @{$self->[IX_LIST]};
4073}
4074
02d1d628
AMH
40751;
4076__END__
4077# Below is the stub of documentation for your module. You better edit it!
4078
4079=head1 NAME
4080
4081Imager - Perl extension for Generating 24 bit Images
4082
4083=head1 SYNOPSIS
4084
0e418f1e
AMH
4085 # Thumbnail example
4086
4087 #!/usr/bin/perl -w
4088 use strict;
10461f9a 4089 use Imager;
02d1d628 4090
0e418f1e
AMH
4091 die "Usage: thumbmake.pl filename\n" if !-f $ARGV[0];
4092 my $file = shift;
4093
4094 my $format;
4095
e36d02ad 4096 # see Imager::Files for information on the read() method
c7cf66c9 4097 my $img = Imager->new(file=>$file)
2f2a6e54 4098 or die Imager->errstr();
0e418f1e
AMH
4099
4100 $file =~ s/\.[^.]*$//;
4101
4102 # Create smaller version
cf7a7d18 4103 # documented in Imager::Transformations
0e418f1e
AMH
4104 my $thumb = $img->scale(scalefactor=>.3);
4105
4106 # Autostretch individual channels
4107 $thumb->filter(type=>'autolevels');
4108
4109 # try to save in one of these formats
4110 SAVE:
4111
c3be83fe 4112 for $format ( qw( png gif jpeg tiff ppm ) ) {
0e418f1e
AMH
4113 # Check if given format is supported
4114 if ($Imager::formats{$format}) {
4115 $file.="_low.$format";
4116 print "Storing image as: $file\n";
cf7a7d18 4117 # documented in Imager::Files
0e418f1e
AMH
4118 $thumb->write(file=>$file) or
4119 die $thumb->errstr;