test and add error reporting to to_*() family methods
[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
3bcba6df
TC
971 $self->_valid_image
972 or return;
faa9b3e7 973
3bcba6df
TC
974 my $result = Imager->new;
975 unless ($result->{IMG} = i_img_to_pal($self->{IMG}, $opts)) {
976 $self->_set_error(Imager->_error_as_msg);
1501d9b3
TC
977 return;
978 }
3bcba6df
TC
979
980 return $result;
faa9b3e7
TC
981}
982
3bcba6df 983# convert a paletted (or any image) to an 8-bit/channel RGB image
faa9b3e7
TC
984sub to_rgb8 {
985 my $self = shift;
faa9b3e7 986
34b3f7e6
TC
987 unless (defined wantarray) {
988 my @caller = caller;
b13bf7e8 989 warn "to_rgb8() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
34b3f7e6
TC
990 return;
991 }
992
3bcba6df
TC
993 $self->_valid_image
994 or return;
995
996 my $result = Imager->new;
997 unless ($result->{IMG} = i_img_to_rgb($self->{IMG})) {
998 $self->_set_error(Imager->_error_as_msg());
999 return;
faa9b3e7
TC
1000 }
1001
1002 return $result;
1003}
1004
3bcba6df 1005# convert a paletted (or any image) to a 16-bit/channel RGB image
837a4b43
TC
1006sub to_rgb16 {
1007 my $self = shift;
837a4b43
TC
1008
1009 unless (defined wantarray) {
1010 my @caller = caller;
3bcba6df 1011 warn "to_rgb16() called in void context - to_rgb16() returns the converted image at $caller[1] line $caller[2]\n";
837a4b43
TC
1012 return;
1013 }
1014
3bcba6df
TC
1015 $self->_valid_image
1016 or return;
1017
1018 my $result = Imager->new;
1019 unless ($result->{IMG} = i_img_to_rgb16($self->{IMG})) {
1020 $self->_set_error(Imager->_error_as_msg());
1021 return;
837a4b43
TC
1022 }
1023
1024 return $result;
1025}
1026
faa9b3e7
TC
1027sub addcolors {
1028 my $self = shift;
1029 my %opts = (colors=>[], @_);
1030
32b97571
TC
1031 unless ($self->{IMG}) {
1032 $self->_set_error("empty input image");
1033 return;
1034 }
1035
1036 my @colors = @{$opts{colors}}
1037 or return undef;
faa9b3e7 1038
32b97571
TC
1039 for my $color (@colors) {
1040 $color = _color($color);
1041 unless ($color) {
1042 $self->_set_error($Imager::ERRSTR);
1043 return;
1044 }
1045 }
1046
1047 return i_addcolors($self->{IMG}, @colors);
faa9b3e7
TC
1048}
1049
1050sub setcolors {
1051 my $self = shift;
1052 my %opts = (start=>0, colors=>[], @_);
faa9b3e7 1053
32b97571
TC
1054 unless ($self->{IMG}) {
1055 $self->_set_error("empty input image");
1056 return;
1057 }
1058
1059 my @colors = @{$opts{colors}}
1060 or return undef;
1061
1062 for my $color (@colors) {
1063 $color = _color($color);
1064 unless ($color) {
1065 $self->_set_error($Imager::ERRSTR);
1066 return;
1067 }
1068 }
1069
1070 return i_setcolors($self->{IMG}, $opts{start}, @colors);
faa9b3e7
TC
1071}
1072
1073sub getcolors {
1074 my $self = shift;
1075 my %opts = @_;
1076 if (!exists $opts{start} && !exists $opts{count}) {
1077 # get them all
1078 $opts{start} = 0;
1079 $opts{count} = $self->colorcount;
1080 }
1081 elsif (!exists $opts{count}) {
1082 $opts{count} = 1;
1083 }
1084 elsif (!exists $opts{start}) {
1085 $opts{start} = 0;
1086 }
1087
1088 $self->{IMG} and
1089 return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
1090}
1091
1092sub colorcount {
1093 i_colorcount($_[0]{IMG});
1094}
1095
1096sub maxcolors {
1097 i_maxcolors($_[0]{IMG});
1098}
1099
1100sub findcolor {
1101 my $self = shift;
1102 my %opts = @_;
1103 $opts{color} or return undef;
1104
1105 $self->{IMG} and i_findcolor($self->{IMG}, $opts{color});
1106}
1107
1108sub bits {
1109 my $self = shift;
af3c2450
TC
1110 my $bits = $self->{IMG} && i_img_bits($self->{IMG});
1111 if ($bits && $bits == length(pack("d", 1)) * 8) {
1112 $bits = 'double';
1113 }
1114 $bits;
faa9b3e7
TC
1115}
1116
1117sub type {
1118 my $self = shift;
1119 if ($self->{IMG}) {
1120 return i_img_type($self->{IMG}) ? "paletted" : "direct";
1121 }
1122}
1123
1124sub virtual {
1125 my $self = shift;
1126 $self->{IMG} and i_img_virtual($self->{IMG});
1127}
1128
bd8052a6
TC
1129sub is_bilevel {
1130 my ($self) = @_;
1131
1132 $self->{IMG} or return;
1133
1134 return i_img_is_monochrome($self->{IMG});
1135}
1136
faa9b3e7
TC
1137sub tags {
1138 my ($self, %opts) = @_;
1139
1140 $self->{IMG} or return;
1141
1142 if (defined $opts{name}) {
1143 my @result;
1144 my $start = 0;
1145 my $found;
1146 while (defined($found = i_tags_find($self->{IMG}, $opts{name}, $start))) {
1147 push @result, (i_tags_get($self->{IMG}, $found))[1];
1148 $start = $found+1;
1149 }
1150 return wantarray ? @result : $result[0];
1151 }
1152 elsif (defined $opts{code}) {
1153 my @result;
1154 my $start = 0;
1155 my $found;
1156 while (defined($found = i_tags_findn($self->{IMG}, $opts{code}, $start))) {
1157 push @result, (i_tags_get($self->{IMG}, $found))[1];
1158 $start = $found+1;
1159 }
1160 return @result;
1161 }
1162 else {
1163 if (wantarray) {
1164 return map { [ i_tags_get($self->{IMG}, $_) ] } 0.. i_tags_count($self->{IMG})-1;
1165 }
1166 else {
1167 return i_tags_count($self->{IMG});
1168 }
1169 }
1170}
1171
1172sub addtag {
1173 my $self = shift;
1174 my %opts = @_;
1175
1176 return -1 unless $self->{IMG};
1177 if ($opts{name}) {
1178 if (defined $opts{value}) {
1179 if ($opts{value} =~ /^\d+$/) {
1180 # add as a number
1181 return i_tags_addn($self->{IMG}, $opts{name}, 0, $opts{value});
1182 }
1183 else {
1184 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{value}, 0);
1185 }
1186 }
1187 elsif (defined $opts{data}) {
1188 # force addition as a string
1189 return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{data}, 0);
1190 }
1191 else {
1192 $self->{ERRSTR} = "No value supplied";
1193 return undef;
1194 }
1195 }
1196 elsif ($opts{code}) {
1197 if (defined $opts{value}) {
1198 if ($opts{value} =~ /^\d+$/) {
1199 # add as a number
1200 return i_tags_addn($self->{IMG}, $opts{code}, 0, $opts{value});
1201 }
1202 else {
1203 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{value}, 0);
1204 }
1205 }
1206 elsif (defined $opts{data}) {
1207 # force addition as a string
1208 return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{data}, 0);
1209 }
1210 else {
1211 $self->{ERRSTR} = "No value supplied";
1212 return undef;
1213 }
1214 }
1215 else {
1216 return undef;
1217 }
1218}
1219
1220sub deltag {
1221 my $self = shift;
1222 my %opts = @_;
1223
1224 return 0 unless $self->{IMG};
1225
9d540150
TC
1226 if (defined $opts{'index'}) {
1227 return i_tags_delete($self->{IMG}, $opts{'index'});
faa9b3e7
TC
1228 }
1229 elsif (defined $opts{name}) {
1230 return i_tags_delbyname($self->{IMG}, $opts{name});
1231 }
1232 elsif (defined $opts{code}) {
1233 return i_tags_delbycode($self->{IMG}, $opts{code});
1234 }
1235 else {
1236 $self->{ERRSTR} = "Need to supply index, name, or code parameter";
1237 return 0;
1238 }
02d1d628
AMH
1239}
1240
97c4effc
TC
1241sub settag {
1242 my ($self, %opts) = @_;
1243
1244 if ($opts{name}) {
1245 $self->deltag(name=>$opts{name});
1246 return $self->addtag(name=>$opts{name}, value=>$opts{value});
1247 }
1248 elsif (defined $opts{code}) {
1249 $self->deltag(code=>$opts{code});
1250 return $self->addtag(code=>$opts{code}, value=>$opts{value});
1251 }
1252 else {
1253 return undef;
1254 }
1255}
1256
10461f9a
TC
1257
1258sub _get_reader_io {
84e51293 1259 my ($self, $input) = @_;
10461f9a 1260
e7ff1cf7
TC
1261 if ($input->{io}) {
1262 return $input->{io}, undef;
1263 }
84e51293 1264 elsif ($input->{fd}) {
10461f9a
TC
1265 return io_new_fd($input->{fd});
1266 }
1267 elsif ($input->{fh}) {
1268 my $fd = fileno($input->{fh});
de470892 1269 unless (defined $fd) {
10461f9a
TC
1270 $self->_set_error("Handle in fh option not opened");
1271 return;
1272 }
1273 return io_new_fd($fd);
1274 }
1275 elsif ($input->{file}) {
1276 my $file = IO::File->new($input->{file}, "r");
1277 unless ($file) {
1278 $self->_set_error("Could not open $input->{file}: $!");
1279 return;
1280 }
1281 binmode $file;
1282 return (io_new_fd(fileno($file)), $file);
1283 }
1284 elsif ($input->{data}) {
1285 return io_new_buffer($input->{data});
1286 }
1287 elsif ($input->{callback} || $input->{readcb}) {
84e51293
AMH
1288 if (!$input->{seekcb}) {
1289 $self->_set_error("Need a seekcb parameter");
10461f9a
TC
1290 }
1291 if ($input->{maxbuffer}) {
1292 return io_new_cb($input->{writecb},
1293 $input->{callback} || $input->{readcb},
1294 $input->{seekcb}, $input->{closecb},
1295 $input->{maxbuffer});
1296 }
1297 else {
1298 return io_new_cb($input->{writecb},
1299 $input->{callback} || $input->{readcb},
1300 $input->{seekcb}, $input->{closecb});
1301 }
1302 }
1303 else {
1304 $self->_set_error("file/fd/fh/data/callback parameter missing");
1305 return;
1306 }
1307}
1308
1309sub _get_writer_io {
1310 my ($self, $input, $type) = @_;
1311
e7ff1cf7
TC
1312 if ($input->{io}) {
1313 return $input->{io};
1314 }
1315 elsif ($input->{fd}) {
10461f9a
TC
1316 return io_new_fd($input->{fd});
1317 }
1318 elsif ($input->{fh}) {
1319 my $fd = fileno($input->{fh});
de470892 1320 unless (defined $fd) {
10461f9a
TC
1321 $self->_set_error("Handle in fh option not opened");
1322 return;
1323 }
9d1c4956
TC
1324 # flush it
1325 my $oldfh = select($input->{fh});
1326 # flush anything that's buffered, and make sure anything else is flushed
1327 $| = 1;
1328 select($oldfh);
10461f9a
TC
1329 return io_new_fd($fd);
1330 }
1331 elsif ($input->{file}) {
1332 my $fh = new IO::File($input->{file},"w+");
1333 unless ($fh) {
1334 $self->_set_error("Could not open file $input->{file}: $!");
1335 return;
1336 }
1337 binmode($fh) or die;
1338 return (io_new_fd(fileno($fh)), $fh);
1339 }
1340 elsif ($input->{data}) {
1341 return io_new_bufchain();
1342 }
1343 elsif ($input->{callback} || $input->{writecb}) {
1344 if ($input->{maxbuffer}) {
1345 return io_new_cb($input->{callback} || $input->{writecb},
1346 $input->{readcb},
1347 $input->{seekcb}, $input->{closecb},
1348 $input->{maxbuffer});
1349 }
1350 else {
1351 return io_new_cb($input->{callback} || $input->{writecb},
1352 $input->{readcb},
1353 $input->{seekcb}, $input->{closecb});
1354 }
1355 }
1356 else {
1357 $self->_set_error("file/fd/fh/data/callback parameter missing");
1358 return;
1359 }
1360}
1361
02d1d628
AMH
1362# Read an image from file
1363
1364sub read {
1365 my $self = shift;
1366 my %input=@_;
02d1d628
AMH
1367
1368 if (defined($self->{IMG})) {
faa9b3e7
TC
1369 # let IIM_DESTROY do the destruction, since the image may be
1370 # referenced from elsewhere
1371 #i_img_destroy($self->{IMG});
02d1d628
AMH
1372 undef($self->{IMG});
1373 }
1374
84e51293
AMH
1375 my ($IO, $fh) = $self->_get_reader_io(\%input) or return;
1376
10461f9a 1377 unless ($input{'type'}) {
66614d6e
TC
1378 $input{'type'} = i_test_format_probe($IO, -1);
1379 }
84e51293
AMH
1380
1381 unless ($input{'type'}) {
1382 $self->_set_error('type parameter missing and not possible to guess from extension');
10461f9a
TC
1383 return undef;
1384 }
02d1d628 1385
53a6bbd4
TC
1386 _reader_autoload($input{type});
1387
1388 if ($readers{$input{type}} && $readers{$input{type}}{single}) {
1389 return $readers{$input{type}}{single}->($self, $IO, %input);
1390 }
1391
1d7e3124 1392 unless ($formats_low{$input{'type'}}) {
f245645a
TC
1393 my $read_types = join ', ', sort Imager->read_types();
1394 $self->_set_error("format '$input{'type'}' not supported - formats $read_types available for reading");
66614d6e
TC
1395 return;
1396 }
1397
d87dc9a4
TC
1398 my $allow_incomplete = $input{allow_incomplete};
1399 defined $allow_incomplete or $allow_incomplete = 0;
9c106321 1400
2fe0b227 1401 if ( $input{'type'} eq 'pnm' ) {
d87dc9a4 1402 $self->{IMG}=i_readpnm_wiol( $IO, $allow_incomplete );
2fe0b227 1403 if ( !defined($self->{IMG}) ) {
2691d220
TC
1404 $self->{ERRSTR}='unable to read pnm image: '._error_as_msg();
1405 return undef;
790923a4 1406 }
2fe0b227
AMH
1407 $self->{DEBUG} && print "loading a pnm file\n";
1408 return $self;
1409 }
790923a4 1410
2fe0b227 1411 if ( $input{'type'} eq 'bmp' ) {
d87dc9a4 1412 $self->{IMG}=i_readbmp_wiol( $IO, $allow_incomplete );
2fe0b227
AMH
1413 if ( !defined($self->{IMG}) ) {
1414 $self->{ERRSTR}=$self->_error_as_msg();
1415 return undef;
10461f9a 1416 }
2fe0b227
AMH
1417 $self->{DEBUG} && print "loading a bmp file\n";
1418 }
10461f9a 1419
2fe0b227
AMH
1420 if ( $input{'type'} eq 'gif' ) {
1421 if ($input{colors} && !ref($input{colors})) {
1422 # must be a reference to a scalar that accepts the colour map
1423 $self->{ERRSTR} = "option 'colors' must be a scalar reference";
1424 return undef;
1ec86afa 1425 }
f1adece7
TC
1426 if ($input{'gif_consolidate'}) {
1427 if ($input{colors}) {
1428 my $colors;
1429 ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
1430 if ($colors) {
1431 ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
1432 }
1433 }
1434 else {
1435 $self->{IMG} =i_readgif_wiol( $IO );
737a830c 1436 }
737a830c 1437 }
2fe0b227 1438 else {
f1adece7
TC
1439 my $page = $input{'page'};
1440 defined $page or $page = 0;
1441 $self->{IMG} = i_readgif_single_wiol( $IO, $page );
a5dbf458 1442 if ($self->{IMG} && $input{colors}) {
f1adece7
TC
1443 ${ $input{colors} } =
1444 [ i_getcolors($self->{IMG}, 0, i_colorcount($self->{IMG})) ];
1445 }
895dbd34 1446 }
f1adece7 1447
2fe0b227
AMH
1448 if ( !defined($self->{IMG}) ) {
1449 $self->{ERRSTR}=$self->_error_as_msg();
1450 return undef;
895dbd34 1451 }
2fe0b227
AMH
1452 $self->{DEBUG} && print "loading a gif file\n";
1453 }
895dbd34 1454
2fe0b227
AMH
1455 if ( $input{'type'} eq 'tga' ) {
1456 $self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
1457 if ( !defined($self->{IMG}) ) {
1458 $self->{ERRSTR}=$self->_error_as_msg();
1459 return undef;
895dbd34 1460 }
2fe0b227
AMH
1461 $self->{DEBUG} && print "loading a tga file\n";
1462 }
02d1d628 1463
2fe0b227 1464 if ( $input{'type'} eq 'raw' ) {
500888da
TC
1465 unless ( $input{xsize} && $input{ysize} ) {
1466 $self->_set_error('missing xsize or ysize parameter for raw');
2fe0b227 1467 return undef;
895dbd34
AMH
1468 }
1469
500888da
TC
1470 my $interleave = _first($input{raw_interleave}, $input{interleave});
1471 unless (defined $interleave) {
1472 my @caller = caller;
1473 warn "read(type => 'raw') $caller[2] line $caller[1]: supply interleave or raw_interleave for future compatibility\n";
1474 $interleave = 1;
1475 }
1476 my $data_ch = _first($input{raw_datachannels}, $input{datachannels}, 3);
1477 my $store_ch = _first($input{raw_storechannels}, $input{storechannels}, 3);
1478
2fe0b227 1479 $self->{IMG} = i_readraw_wiol( $IO,
500888da
TC
1480 $input{xsize},
1481 $input{ysize},
1482 $data_ch,
1483 $store_ch,
1484 $interleave);
2fe0b227 1485 if ( !defined($self->{IMG}) ) {
5f8f8e17 1486 $self->{ERRSTR}=$self->_error_as_msg();
2fe0b227 1487 return undef;
dd55acc8 1488 }
2fe0b227 1489 $self->{DEBUG} && print "loading a raw file\n";
02d1d628 1490 }
2fe0b227 1491
02d1d628 1492 return $self;
02d1d628
AMH
1493}
1494
53a6bbd4
TC
1495sub register_reader {
1496 my ($class, %opts) = @_;
1497
1498 defined $opts{type}
1499 or die "register_reader called with no type parameter\n";
1500
1501 my $type = $opts{type};
1502
1503 defined $opts{single} || defined $opts{multiple}
1504 or die "register_reader called with no single or multiple parameter\n";
1505
1506 $readers{$type} = { };
1507 if ($opts{single}) {
1508 $readers{$type}{single} = $opts{single};
1509 }
1510 if ($opts{multiple}) {
1511 $readers{$type}{multiple} = $opts{multiple};
1512 }
1513
1514 return 1;
1515}
1516
2b405c9e
TC
1517sub register_writer {
1518 my ($class, %opts) = @_;
1519
1520 defined $opts{type}
1521 or die "register_writer called with no type parameter\n";
1522
1523 my $type = $opts{type};
1524
1525 defined $opts{single} || defined $opts{multiple}
1526 or die "register_writer called with no single or multiple parameter\n";
1527
1528 $writers{$type} = { };
1529 if ($opts{single}) {
1530 $writers{$type}{single} = $opts{single};
1531 }
1532 if ($opts{multiple}) {
1533 $writers{$type}{multiple} = $opts{multiple};
1534 }
1535
1536 return 1;
1537}
1538
f245645a
TC
1539sub read_types {
1540 my %types =
1541 (
1542 map { $_ => 1 }
1543 keys %readers,
1544 grep($file_formats{$_}, keys %formats),
1545 qw(ico sgi), # formats not handled directly, but supplied with Imager
1546 );
1547
1548 return keys %types;
1549}
1550
1551sub write_types {
1552 my %types =
1553 (
1554 map { $_ => 1 }
1555 keys %writers,
1556 grep($file_formats{$_}, keys %formats),
1557 qw(ico sgi), # formats not handled directly, but supplied with Imager
1558 );
1559
1560 return keys %types;
1561}
1562
53a6bbd4
TC
1563# probes for an Imager::File::whatever module
1564sub _reader_autoload {
1565 my $type = shift;
1566
1d7e3124 1567 return if $formats_low{$type} || $readers{$type};
53a6bbd4
TC
1568
1569 return unless $type =~ /^\w+$/;
1570
1571 my $file = "Imager/File/\U$type\E.pm";
1572
1573 unless ($attempted_to_load{$file}) {
1574 eval {
1575 ++$attempted_to_load{$file};
1576 require $file;
1577 };
2b405c9e
TC
1578 if ($@) {
1579 # try to get a reader specific module
1580 my $file = "Imager/File/\U$type\EReader.pm";
1581 unless ($attempted_to_load{$file}) {
1582 eval {
1583 ++$attempted_to_load{$file};
1584 require $file;
1585 };
1586 }
1587 }
1588 }
1589}
1590
1591# probes for an Imager::File::whatever module
1592sub _writer_autoload {
1593 my $type = shift;
1594
1d7e3124 1595 return if $formats_low{$type} || $readers{$type};
2b405c9e
TC
1596
1597 return unless $type =~ /^\w+$/;
1598
1599 my $file = "Imager/File/\U$type\E.pm";
1600
1601 unless ($attempted_to_load{$file}) {
1602 eval {
1603 ++$attempted_to_load{$file};
1604 require $file;
1605 };
1606 if ($@) {
1607 # try to get a writer specific module
1608 my $file = "Imager/File/\U$type\EWriter.pm";
1609 unless ($attempted_to_load{$file}) {
1610 eval {
1611 ++$attempted_to_load{$file};
1612 require $file;
1613 };
1614 }
1615 }
53a6bbd4
TC
1616 }
1617}
1618
97c4effc
TC
1619sub _fix_gif_positions {
1620 my ($opts, $opt, $msg, @imgs) = @_;
2fe0b227 1621
97c4effc
TC
1622 my $positions = $opts->{'gif_positions'};
1623 my $index = 0;
1624 for my $pos (@$positions) {
1625 my ($x, $y) = @$pos;
1626 my $img = $imgs[$index++];
9d1c4956
TC
1627 $img->settag(name=>'gif_left', value=>$x);
1628 $img->settag(name=>'gif_top', value=>$y) if defined $y;
97c4effc
TC
1629 }
1630 $$msg .= "replaced with the gif_left and gif_top tags";
1631}
1632
1633my %obsolete_opts =
1634 (
1635 gif_each_palette=>'gif_local_map',
1636 interlace => 'gif_interlace',
1637 gif_delays => 'gif_delay',
1638 gif_positions => \&_fix_gif_positions,
1639 gif_loop_count => 'gif_loop',
1640 );
1641
6e4af7d4
TC
1642# options that should be converted to colors
1643my %color_opts = map { $_ => 1 } qw/i_background/;
1644
97c4effc
TC
1645sub _set_opts {
1646 my ($self, $opts, $prefix, @imgs) = @_;
1647
1648 for my $opt (keys %$opts) {
1649 my $tagname = $opt;
1650 if ($obsolete_opts{$opt}) {
1651 my $new = $obsolete_opts{$opt};
1652 my $msg = "Obsolete option $opt ";
1653 if (ref $new) {
1654 $new->($opts, $opt, \$msg, @imgs);
1655 }
1656 else {
1657 $msg .= "replaced with the $new tag ";
1658 $tagname = $new;
1659 }
1660 $msg .= "line ".(caller(2))[2]." of file ".(caller(2))[1];
1661 warn $msg if $warn_obsolete && $^W;
1662 }
1663 next unless $tagname =~ /^\Q$prefix/;
1664 my $value = $opts->{$opt};
6e4af7d4
TC
1665 if ($color_opts{$opt}) {
1666 $value = _color($value);
1667 unless ($value) {
1668 $self->_set_error($Imager::ERRSTR);
1669 return;
1670 }
1671 }
97c4effc
TC
1672 if (ref $value) {
1673 if (UNIVERSAL::isa($value, "Imager::Color")) {
1674 my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
1675 for my $img (@imgs) {
1676 $img->settag(name=>$tagname, value=>$tag);
1677 }
1678 }
1679 elsif (ref($value) eq 'ARRAY') {
1680 for my $i (0..$#$value) {
1681 my $val = $value->[$i];
1682 if (ref $val) {
1683 if (UNIVERSAL::isa($val, "Imager::Color")) {
1684 my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
1685 $i < @imgs and
1686 $imgs[$i]->settag(name=>$tagname, value=>$tag);
1687 }
1688 else {
1689 $self->_set_error("Unknown reference type " . ref($value) .
1690 " supplied in array for $opt");
1691 return;
1692 }
1693 }
1694 else {
1695 $i < @imgs
1696 and $imgs[$i]->settag(name=>$tagname, value=>$val);
1697 }
1698 }
1699 }
1700 else {
1701 $self->_set_error("Unknown reference type " . ref($value) .
1702 " supplied for $opt");
1703 return;
1704 }
1705 }
1706 else {
1707 # set it as a tag for every image
1708 for my $img (@imgs) {
1709 $img->settag(name=>$tagname, value=>$value);
1710 }
1711 }
1712 }
1713
1714 return 1;
1715}
1716
02d1d628 1717# Write an image to file
02d1d628
AMH
1718sub write {
1719 my $self = shift;
2fe0b227
AMH
1720 my %input=(jpegquality=>75,
1721 gifquant=>'mc',
1722 lmdither=>6.0,
febba01f
AMH
1723 lmfixed=>[],
1724 idstring=>"",
1725 compress=>1,
1726 wierdpack=>0,
4c2d6970 1727 fax_fine=>1, @_);
10461f9a 1728 my $rc;
02d1d628 1729
97c4effc
TC
1730 $self->_set_opts(\%input, "i_", $self)
1731 or return undef;
1732
02d1d628
AMH
1733 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1734
9d540150
TC
1735 if (!$input{'type'} and $input{file}) {
1736 $input{'type'}=$FORMATGUESS->($input{file});
1737 }
1738 if (!$input{'type'}) {
1739 $self->{ERRSTR}='type parameter missing and not possible to guess from extension';
1740 return undef;
1741 }
02d1d628 1742
2b405c9e 1743 _writer_autoload($input{type});
02d1d628 1744
2b405c9e
TC
1745 my ($IO, $fh);
1746 if ($writers{$input{type}} && $writers{$input{type}}{single}) {
1747 ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
2fe0b227 1748 or return undef;
febba01f 1749
2b405c9e 1750 $writers{$input{type}}{single}->($self, $IO, %input)
2fe0b227 1751 or return undef;
2b405c9e
TC
1752 }
1753 else {
1d7e3124 1754 if (!$formats_low{$input{'type'}}) {
f245645a
TC
1755 my $write_types = join ', ', sort Imager->write_types();
1756 $self->_set_error("format '$input{'type'}' not supported - formats $write_types available for writing");
2fe0b227 1757 return undef;
930c67c8 1758 }
2b405c9e
TC
1759
1760 ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
2fe0b227 1761 or return undef;
2b405c9e 1762
e5ee047b 1763 if ( $input{'type'} eq 'pnm' ) {
2b405c9e
TC
1764 $self->_set_opts(\%input, "pnm_", $self)
1765 or return undef;
1766 if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
1767 $self->{ERRSTR} = $self->_error_as_msg();
1768 return undef;
1769 }
1770 $self->{DEBUG} && print "writing a pnm file\n";
1771 } elsif ( $input{'type'} eq 'raw' ) {
1772 $self->_set_opts(\%input, "raw_", $self)
1773 or return undef;
1774 if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
1775 $self->{ERRSTR} = $self->_error_as_msg();
1776 return undef;
1777 }
1778 $self->{DEBUG} && print "writing a raw file\n";
2b405c9e
TC
1779 } elsif ( $input{'type'} eq 'jpeg' ) {
1780 $self->_set_opts(\%input, "jpeg_", $self)
1781 or return undef;
1782 $self->_set_opts(\%input, "exif_", $self)
1783 or return undef;
1784 if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
1785 $self->{ERRSTR} = $self->_error_as_msg();
1786 return undef;
1787 }
1788 $self->{DEBUG} && print "writing a jpeg file\n";
1789 } elsif ( $input{'type'} eq 'bmp' ) {
1790 $self->_set_opts(\%input, "bmp_", $self)
1791 or return undef;
1792 if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
ae12796a 1793 $self->{ERRSTR} = $self->_error_as_msg;
2b405c9e
TC
1794 return undef;
1795 }
1796 $self->{DEBUG} && print "writing a bmp file\n";
1797 } elsif ( $input{'type'} eq 'tga' ) {
1798 $self->_set_opts(\%input, "tga_", $self)
1799 or return undef;
1800
1801 if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
1802 $self->{ERRSTR}=$self->_error_as_msg();
1803 return undef;
1804 }
1805 $self->{DEBUG} && print "writing a tga file\n";
1806 } elsif ( $input{'type'} eq 'gif' ) {
1807 $self->_set_opts(\%input, "gif_", $self)
1808 or return undef;
1809 # compatibility with the old interfaces
1810 if ($input{gifquant} eq 'lm') {
1811 $input{make_colors} = 'addi';
1812 $input{translate} = 'perturb';
1813 $input{perturb} = $input{lmdither};
1814 } elsif ($input{gifquant} eq 'gen') {
1815 # just pass options through
1816 } else {
1817 $input{make_colors} = 'webmap'; # ignored
1818 $input{translate} = 'giflib';
1819 }
1820 if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
1821 $self->{ERRSTR} = $self->_error_as_msg;
1822 return;
1823 }
1501d9b3 1824 }
02d1d628 1825 }
10461f9a 1826
2fe0b227
AMH
1827 if (exists $input{'data'}) {
1828 my $data = io_slurp($IO);
1829 if (!$data) {
1830 $self->{ERRSTR}='Could not slurp from buffer';
1831 return undef;
1832 }
1833 ${$input{data}} = $data;
1834 }
02d1d628
AMH
1835 return $self;
1836}
1837
1838sub write_multi {
1839 my ($class, $opts, @images) = @_;
1840
2b405c9e
TC
1841 my $type = $opts->{type};
1842
1843 if (!$type && $opts->{'file'}) {
1844 $type = $FORMATGUESS->($opts->{'file'});
10461f9a 1845 }
2b405c9e 1846 unless ($type) {
10461f9a
TC
1847 $class->_set_error('type parameter missing and not possible to guess from extension');
1848 return;
1849 }
1850 # translate to ImgRaw
1851 if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
1852 $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
1853 return 0;
1854 }
97c4effc
TC
1855 $class->_set_opts($opts, "i_", @images)
1856 or return;
10461f9a 1857 my @work = map $_->{IMG}, @images;
2b405c9e
TC
1858
1859 _writer_autoload($type);
1860
1861 my ($IO, $file);
1862 if ($writers{$type} && $writers{$type}{multiple}) {
1863 ($IO, $file) = $class->_get_writer_io($opts, $type)
1864 or return undef;
1865
1866 $writers{$type}{multiple}->($class, $IO, $opts, @images)
1867 or return undef;
1868 }
1869 else {
1870 if (!$formats{$type}) {
f245645a
TC
1871 my $write_types = join ', ', sort Imager->write_types();
1872 $class->_set_error("format '$type' not supported - formats $write_types available for writing");
2b405c9e
TC
1873 return undef;
1874 }
1875
1876 ($IO, $file) = $class->_get_writer_io($opts, $type)
1877 or return undef;
1878
e5ee047b 1879 if (0) { # eventually PNM in here, now that TIFF/GIF are elsewhere
02d1d628
AMH
1880 }
1881 else {
e7ff1cf7
TC
1882 if (@images == 1) {
1883 unless ($images[0]->write(%$opts, io => $IO, type => $type)) {
1884 return 1;
1885 }
1886 }
1887 else {
1888 $ERRSTR = "Sorry, write_multi doesn't support $type yet";
1889 return 0;
1890 }
02d1d628
AMH
1891 }
1892 }
2b405c9e
TC
1893
1894 if (exists $opts->{'data'}) {
1895 my $data = io_slurp($IO);
1896 if (!$data) {
1897 Imager->_set_error('Could not slurp from buffer');
1898 return undef;
1899 }
1900 ${$opts->{data}} = $data;
02d1d628 1901 }
2b405c9e 1902 return 1;
02d1d628
AMH
1903}
1904
faa9b3e7
TC
1905# read multiple images from a file
1906sub read_multi {
1907 my ($class, %opts) = @_;
1908
53a6bbd4
TC
1909 my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
1910 or return;
1911
1912 my $type = $opts{'type'};
1913 unless ($type) {
1914 $type = i_test_format_probe($IO, -1);
1915 }
1916
1917 if ($opts{file} && !$type) {
faa9b3e7 1918 # guess the type
53a6bbd4 1919 $type = $FORMATGUESS->($opts{file});
faa9b3e7 1920 }
53a6bbd4
TC
1921
1922 unless ($type) {
faa9b3e7
TC
1923 $ERRSTR = "No type parameter supplied and it couldn't be guessed";
1924 return;
1925 }
faa9b3e7 1926
53a6bbd4
TC
1927 _reader_autoload($type);
1928
1929 if ($readers{$type} && $readers{$type}{multiple}) {
1930 return $readers{$type}{multiple}->($IO, %opts);
1931 }
1932
8d46e5da
TC
1933 unless ($formats{$type}) {
1934 my $read_types = join ', ', sort Imager->read_types();
1935 Imager->_set_error("format '$type' not supported - formats $read_types available for reading");
1936 return;
1937 }
1938
e5ee047b
TC
1939 my @imgs;
1940 if ($type eq 'pnm') {
2086be61 1941 @imgs = i_readpnm_multi_wiol($IO, $opts{allow_incomplete}||0);
faa9b3e7 1942 }
e7ff1cf7
TC
1943 else {
1944 my $img = Imager->new;
1945 if ($img->read(%opts, io => $IO, type => $type)) {
1946 return ( $img );
1947 }
f245645a 1948 Imager->_set_error($img->errstr);
2086be61 1949 return;
e7ff1cf7 1950 }
faa9b3e7 1951
2086be61
TC
1952 if (!@imgs) {
1953 $ERRSTR = _error_as_msg();
faa9b3e7 1954 return;
2086be61
TC
1955 }
1956 return map {
1957 bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
1958 } @imgs;
faa9b3e7
TC
1959}
1960
02d1d628
AMH
1961# Destroy an Imager object
1962
1963sub DESTROY {
1964 my $self=shift;
1965 # delete $instances{$self};
1966 if (defined($self->{IMG})) {
faa9b3e7
TC
1967 # the following is now handled by the XS DESTROY method for
1968 # Imager::ImgRaw object
1969 # Re-enabling this will break virtual images
1970 # tested for in t/t020masked.t
1971 # i_img_destroy($self->{IMG});
02d1d628
AMH
1972 undef($self->{IMG});
1973 } else {
1974# print "Destroy Called on an empty image!\n"; # why did I put this here??
1975 }
1976}
1977
1978# Perform an inplace filter of an image
1979# that is the image will be overwritten with the data
1980
1981sub filter {
1982 my $self=shift;
1983 my %input=@_;
1984 my %hsh;
1985 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
1986
9d540150 1987 if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; }
02d1d628 1988
9d540150 1989 if ( (grep { $_ eq $input{'type'} } keys %filters) != 1) {
02d1d628
AMH
1990 $self->{ERRSTR}='type parameter not matching any filter'; return undef;
1991 }
1992
9d540150
TC
1993 if ($filters{$input{'type'}}{names}) {
1994 my $names = $filters{$input{'type'}}{names};
6607600c
TC
1995 for my $name (keys %$names) {
1996 if (defined $input{$name} && exists $names->{$name}{$input{$name}}) {
1997 $input{$name} = $names->{$name}{$input{$name}};
1998 }
1999 }
2000 }
9d540150 2001 if (defined($filters{$input{'type'}}{defaults})) {
7327d4b0
TC
2002 %hsh=( image => $self->{IMG},
2003 imager => $self,
2004 %{$filters{$input{'type'}}{defaults}},
2005 %input );
02d1d628 2006 } else {
7327d4b0
TC
2007 %hsh=( image => $self->{IMG},
2008 imager => $self,
2009 %input );
02d1d628
AMH
2010 }
2011
9d540150 2012 my @cs=@{$filters{$input{'type'}}{callseq}};
02d1d628
AMH
2013
2014 for(@cs) {
2015 if (!defined($hsh{$_})) {
9d540150 2016 $self->{ERRSTR}="missing parameter '$_' for filter ".$input{'type'}; return undef;
02d1d628
AMH
2017 }
2018 }
2019
109bec2d
TC
2020 eval {
2021 local $SIG{__DIE__}; # we don't want this processed by confess, etc
2022 &{$filters{$input{'type'}}{callsub}}(%hsh);
2023 };
2024 if ($@) {
2025 chomp($self->{ERRSTR} = $@);
2026 return;
2027 }
02d1d628
AMH
2028
2029 my @b=keys %hsh;
2030
2031 $self->{DEBUG} && print "callseq is: @cs\n";
2032 $self->{DEBUG} && print "matching callseq is: @b\n";
2033
2034 return $self;
2035}
2036
92bda632
TC
2037sub register_filter {
2038 my $class = shift;
2039 my %hsh = ( defaults => {}, @_ );
2040
2041 defined $hsh{type}
2042 or die "register_filter() with no type\n";
2043 defined $hsh{callsub}
2044 or die "register_filter() with no callsub\n";
2045 defined $hsh{callseq}
2046 or die "register_filter() with no callseq\n";
2047
2048 exists $filters{$hsh{type}}
2049 and return;
2050
2051 $filters{$hsh{type}} = \%hsh;
2052
2053 return 1;
2054}
2055
df9aaafb
TC
2056sub scale_calculate {
2057 my $self = shift;
02d1d628 2058
df9aaafb 2059 my %opts = ('type'=>'max', @_);
4f579313 2060
de470892
TC
2061 # none of these should be references
2062 for my $name (qw/xpixels ypixels xscalefactor yscalefactor width height/) {
2063 if (defined $opts{$name} && ref $opts{$name}) {
2064 $self->_set_error("scale_calculate: $name parameter cannot be a reference");
2065 return;
2066 }
2067 }
2068
df9aaafb
TC
2069 my ($x_scale, $y_scale);
2070 my $width = $opts{width};
2071 my $height = $opts{height};
2072 if (ref $self) {
2073 defined $width or $width = $self->getwidth;
2074 defined $height or $height = $self->getheight;
ace46df2 2075 }
df9aaafb
TC
2076 else {
2077 unless (defined $width && defined $height) {
2078 $self->_set_error("scale_calculate: width and height parameters must be supplied when called as a class method");
2079 return;
2080 }
5168ca3a 2081 }
02d1d628 2082
658f724e
TC
2083 if ($opts{'xscalefactor'} && $opts{'yscalefactor'}) {
2084 $x_scale = $opts{'xscalefactor'};
2085 $y_scale = $opts{'yscalefactor'};
2086 }
2087 elsif ($opts{'xscalefactor'}) {
2088 $x_scale = $opts{'xscalefactor'};
2089 $y_scale = $opts{'scalefactor'} || $x_scale;
2090 }
2091 elsif ($opts{'yscalefactor'}) {
2092 $y_scale = $opts{'yscalefactor'};
2093 $x_scale = $opts{'scalefactor'} || $y_scale;
2094 }
2095 else {
2096 $x_scale = $y_scale = $opts{'scalefactor'} || 0.5;
2097 }
2098
5168ca3a 2099 # work out the scaling
9d540150 2100 if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
df9aaafb
TC
2101 my ($xpix, $ypix)=( $opts{xpixels} / $width ,
2102 $opts{ypixels} / $height );
5168ca3a 2103 if ($opts{'type'} eq 'min') {
658f724e 2104 $x_scale = $y_scale = _min($xpix,$ypix);
5168ca3a
TC
2105 }
2106 elsif ($opts{'type'} eq 'max') {
658f724e
TC
2107 $x_scale = $y_scale = _max($xpix,$ypix);
2108 }
2109 elsif ($opts{'type'} eq 'nonprop' || $opts{'type'} eq 'non-proportional') {
2110 $x_scale = $xpix;
2111 $y_scale = $ypix;
5168ca3a
TC
2112 }
2113 else {
2114 $self->_set_error('invalid value for type parameter');
df9aaafb 2115 return;
5168ca3a
TC
2116 }
2117 } elsif ($opts{xpixels}) {
df9aaafb 2118 $x_scale = $y_scale = $opts{xpixels} / $width;
5168ca3a
TC
2119 }
2120 elsif ($opts{ypixels}) {
df9aaafb 2121 $x_scale = $y_scale = $opts{ypixels}/$height;
5168ca3a 2122 }
41c7d053
TC
2123 elsif ($opts{constrain} && ref $opts{constrain}
2124 && $opts{constrain}->can('constrain')) {
2125 # we've been passed an Image::Math::Constrain object or something
2126 # that looks like one
658f724e 2127 my $scalefactor;
4f579313 2128 (undef, undef, $scalefactor)
41c7d053 2129 = $opts{constrain}->constrain($self->getwidth, $self->getheight);
4f579313 2130 unless ($scalefactor) {
41c7d053 2131 $self->_set_error('constrain method failed on constrain parameter');
df9aaafb 2132 return;
41c7d053 2133 }
658f724e 2134 $x_scale = $y_scale = $scalefactor;
41c7d053 2135 }
02d1d628 2136
df9aaafb
TC
2137 my $new_width = int($x_scale * $width + 0.5);
2138 $new_width > 0 or $new_width = 1;
2139 my $new_height = int($y_scale * $height + 0.5);
2140 $new_height > 0 or $new_height = 1;
2141
2142 return ($x_scale, $y_scale, $new_width, $new_height);
2143
2144}
2145
2146# Scale an image to requested size and return the scaled version
2147
2148sub scale {
2149 my $self=shift;
2150 my %opts = (qtype=>'normal' ,@_);
2151 my $img = Imager->new();
2152 my $tmp = Imager->new();
2153
2154 unless (defined wantarray) {
2155 my @caller = caller;
2156 warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
2157 return;
2158 }
2159
2160 unless ($self->{IMG}) {
2161 $self->_set_error('empty input image');
2162 return undef;
2163 }
2164
2165 my ($x_scale, $y_scale, $new_width, $new_height) =
2166 $self->scale_calculate(%opts)
2167 or return;
2168
02d1d628 2169 if ($opts{qtype} eq 'normal') {
658f724e 2170 $tmp->{IMG} = i_scaleaxis($self->{IMG}, $x_scale, 0);
5168ca3a 2171 if ( !defined($tmp->{IMG}) ) {
de470892 2172 $self->{ERRSTR} = 'unable to scale image: ' . $self->_error_as_msg;
5168ca3a
TC
2173 return undef;
2174 }
658f724e 2175 $img->{IMG}=i_scaleaxis($tmp->{IMG}, $y_scale, 1);
5168ca3a 2176 if ( !defined($img->{IMG}) ) {
de470892 2177 $self->{ERRSTR}='unable to scale image: ' . $self->_error_as_msg;
5168ca3a
TC
2178 return undef;
2179 }
2180
02d1d628
AMH
2181 return $img;
2182 }
5168ca3a 2183 elsif ($opts{'qtype'} eq 'preview') {
658f724e 2184 $img->{IMG} = i_scale_nn($self->{IMG}, $x_scale, $y_scale);
5168ca3a
TC
2185 if ( !defined($img->{IMG}) ) {
2186 $self->{ERRSTR}='unable to scale image';
2187 return undef;
2188 }
02d1d628
AMH
2189 return $img;
2190 }
658f724e 2191 elsif ($opts{'qtype'} eq 'mixing') {
658f724e
TC
2192 $img->{IMG} = i_scale_mixing($self->{IMG}, $new_width, $new_height);
2193 unless ($img->{IMG}) {
de470892 2194 $self->_set_error(Imager->_error_as_msg);
658f724e
TC
2195 return;
2196 }
2197 return $img;
2198 }
5168ca3a
TC
2199 else {
2200 $self->_set_error('invalid value for qtype parameter');
2201 return undef;
2202 }
02d1d628
AMH
2203}
2204
2205# Scales only along the X axis
2206
2207sub scaleX {
15327bf5
TC
2208 my $self = shift;
2209 my %opts = ( scalefactor=>0.5, @_ );
02d1d628 2210
34b3f7e6
TC
2211 unless (defined wantarray) {
2212 my @caller = caller;
2213 warn "scaleX() called in void context - scaleX() returns the scaled image at $caller[1] line $caller[2]\n";
2214 return;
2215 }
2216
15327bf5
TC
2217 unless ($self->{IMG}) {
2218 $self->{ERRSTR} = 'empty input image';
2219 return undef;
2220 }
02d1d628
AMH
2221
2222 my $img = Imager->new();
2223
15327bf5 2224 my $scalefactor = $opts{scalefactor};
02d1d628 2225
15327bf5
TC
2226 if ($opts{pixels}) {
2227 $scalefactor = $opts{pixels} / $self->getwidth();
2228 }
2229
2230 unless ($self->{IMG}) {
2231 $self->{ERRSTR}='empty input image';
2232 return undef;
2233 }
2234
2235 $img->{IMG} = i_scaleaxis($self->{IMG}, $scalefactor, 0);
2236
2237 if ( !defined($img->{IMG}) ) {
2238 $self->{ERRSTR} = 'unable to scale image';
2239 return undef;
2240 }
02d1d628 2241
02d1d628
AMH
2242 return $img;
2243}
2244
2245# Scales only along the Y axis
2246
2247sub scaleY {
15327bf5
TC
2248 my $self = shift;
2249 my %opts = ( scalefactor => 0.5, @_ );
02d1d628 2250
34b3f7e6
TC
2251 unless (defined wantarray) {
2252 my @caller = caller;
2253 warn "scaleY() called in void context - scaleY() returns the scaled image at $caller[1] line $caller[2]\n";
2254 return;
2255 }
2256
02d1d628
AMH
2257 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2258
2259 my $img = Imager->new();
2260
15327bf5 2261 my $scalefactor = $opts{scalefactor};
02d1d628 2262
15327bf5
TC
2263 if ($opts{pixels}) {
2264 $scalefactor = $opts{pixels} / $self->getheight();
2265 }
2266
2267 unless ($self->{IMG}) {
2268 $self->{ERRSTR} = 'empty input image';
2269 return undef;
2270 }
2271 $img->{IMG}=i_scaleaxis($self->{IMG}, $scalefactor, 1);
2272
2273 if ( !defined($img->{IMG}) ) {
2274 $self->{ERRSTR} = 'unable to scale image';
2275 return undef;
2276 }
02d1d628 2277
02d1d628
AMH
2278 return $img;
2279}
2280
02d1d628
AMH
2281# Transform returns a spatial transformation of the input image
2282# this moves pixels to a new location in the returned image.
2283# NOTE - should make a utility function to check transforms for
2284# stack overruns
2285
2286sub transform {
2287 my $self=shift;
2288 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2289 my %opts=@_;
2290 my (@op,@ropx,@ropy,$iop,$or,@parm,$expr,@xt,@yt,@pt,$numre);
2291
2292# print Dumper(\%opts);
2293# xopcopdes
2294
2295 if ( $opts{'xexpr'} and $opts{'yexpr'} ) {
2296 if (!$I2P) {
2297 eval ("use Affix::Infix2Postfix;");
2298 print $@;
2299 if ( $@ ) {
2300 $self->{ERRSTR}='transform: expr given and Affix::Infix2Postfix is not avaliable.';
2301 return undef;
2302 }
2303 $I2P=Affix::Infix2Postfix->new('ops'=>[{op=>'+',trans=>'Add'},
2304 {op=>'-',trans=>'Sub'},
2305 {op=>'*',trans=>'Mult'},
2306 {op=>'/',trans=>'Div'},
9d540150 2307 {op=>'-','type'=>'unary',trans=>'u-'},
02d1d628 2308 {op=>'**'},
9d540150 2309 {op=>'func','type'=>'unary'}],
02d1d628
AMH
2310 'grouping'=>[qw( \( \) )],
2311 'func'=>[qw( sin cos )],
2312 'vars'=>[qw( x y )]
2313 );
2314 }
2315
2316 @xt=$I2P->translate($opts{'xexpr'});
2317 @yt=$I2P->translate($opts{'yexpr'});
2318
2319 $numre=$I2P->{'numre'};
2320 @pt=(0,0);
2321
2322 for(@xt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'xopcodes'}},'Parm',$#pt); } else { push(@{$opts{'xopcodes'}},$_); } }
2323 for(@yt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'yopcodes'}},'Parm',$#pt); } else { push(@{$opts{'yopcodes'}},$_); } }
2324 @{$opts{'parm'}}=@pt;
2325 }
2326
2327# print Dumper(\%opts);
2328
2329 if ( !exists $opts{'xopcodes'} or @{$opts{'xopcodes'}}==0) {
2330 $self->{ERRSTR}='transform: no xopcodes given.';
2331 return undef;
2332 }
2333
2334 @op=@{$opts{'xopcodes'}};
2335 for $iop (@op) {
2336 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
2337 $self->{ERRSTR}="transform: illegal opcode '$_'.";
2338 return undef;
2339 }
2340 push(@ropx,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
2341 }
2342
2343
2344# yopcopdes
2345
2346 if ( !exists $opts{'yopcodes'} or @{$opts{'yopcodes'}}==0) {
2347 $self->{ERRSTR}='transform: no yopcodes given.';
2348 return undef;
2349 }
2350
2351 @op=@{$opts{'yopcodes'}};
2352 for $iop (@op) {
2353 if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
2354 $self->{ERRSTR}="transform: illegal opcode '$_'.";
2355 return undef;
2356 }
2357 push(@ropy,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
2358 }
2359
2360#parameters
2361
2362 if ( !exists $opts{'parm'}) {
2363 $self->{ERRSTR}='transform: no parameter arg given.';
2364 return undef;
2365 }
2366
2367# print Dumper(\@ropx);
2368# print Dumper(\@ropy);
2369# print Dumper(\@ropy);
2370
2371 my $img = Imager->new();
2372 $img->{IMG}=i_transform($self->{IMG},\@ropx,\@ropy,$opts{'parm'});
2373 if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='transform: failed'; return undef; }
2374 return $img;
2375}
2376
2377
bf94b653
TC
2378sub transform2 {
2379 my ($opts, @imgs) = @_;
2380
2381 require "Imager/Expr.pm";
2382
2383 $opts->{variables} = [ qw(x y) ];
2384 my ($width, $height) = @{$opts}{qw(width height)};
2385 if (@imgs) {
2386 $width ||= $imgs[0]->getwidth();
2387 $height ||= $imgs[0]->getheight();
2388 my $img_num = 1;
2389 for my $img (@imgs) {
2390 $opts->{constants}{"w$img_num"} = $img->getwidth();
2391 $opts->{constants}{"h$img_num"} = $img->getheight();
2392 $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
2393 $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
2394 ++$img_num;
02d1d628 2395 }
02d1d628 2396 }
bf94b653
TC
2397 if ($width) {
2398 $opts->{constants}{w} = $width;
2399 $opts->{constants}{cx} = $width/2;
2400 }
2401 else {
2402 $Imager::ERRSTR = "No width supplied";
2403 return;
2404 }
2405 if ($height) {
2406 $opts->{constants}{h} = $height;
2407 $opts->{constants}{cy} = $height/2;
2408 }
2409 else {
2410 $Imager::ERRSTR = "No height supplied";
2411 return;
2412 }
2413 my $code = Imager::Expr->new($opts);
2414 if (!$code) {
2415 $Imager::ERRSTR = Imager::Expr::error();
2416 return;
2417 }
e5744e01
TC
2418 my $channels = $opts->{channels} || 3;
2419 unless ($channels >= 1 && $channels <= 4) {
2420 return Imager->_set_error("channels must be an integer between 1 and 4");
2421 }
9982a307 2422
bf94b653 2423 my $img = Imager->new();
e5744e01
TC
2424 $img->{IMG} = i_transform2($opts->{width}, $opts->{height},
2425 $channels, $code->code(),
bf94b653
TC
2426 $code->nregs(), $code->cregs(),
2427 [ map { $_->{IMG} } @imgs ]);
2428 if (!defined $img->{IMG}) {
2429 $Imager::ERRSTR = Imager->_error_as_msg();
2430 return;
2431 }
9982a307 2432
bf94b653 2433 return $img;
02d1d628
AMH
2434}
2435
02d1d628
AMH
2436sub rubthrough {
2437 my $self=shift;
9b1ec2b8 2438 my %opts= @_;
02d1d628 2439
e7b95388
TC
2440 unless ($self->{IMG}) {
2441 $self->{ERRSTR}='empty input image';
2442 return undef;
2443 }
2444 unless ($opts{src} && $opts{src}->{IMG}) {
2445 $self->{ERRSTR}='empty input image for src';
2446 return undef;
2447 }
02d1d628 2448
71dc4a83
AMH
2449 %opts = (src_minx => 0,
2450 src_miny => 0,
2451 src_maxx => $opts{src}->getwidth(),
2452 src_maxy => $opts{src}->getheight(),
2453 %opts);
2454
9b1ec2b8
TC
2455 my $tx = $opts{tx};
2456 defined $tx or $tx = $opts{left};
2457 defined $tx or $tx = 0;
2458
2459 my $ty = $opts{ty};
2460 defined $ty or $ty = $opts{top};
2461 defined $ty or $ty = 0;
2462
2463 unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $tx, $ty,
e7b95388
TC
2464 $opts{src_minx}, $opts{src_miny},
2465 $opts{src_maxx}, $opts{src_maxy})) {
2466 $self->_set_error($self->_error_as_msg());
faa9b3e7
TC
2467 return undef;
2468 }
9b1ec2b8 2469
02d1d628
AMH
2470 return $self;
2471}
2472
9b1ec2b8
TC
2473sub compose {
2474 my $self = shift;
2475 my %opts =
2476 (
2477 opacity => 1.0,
2478 mask_left => 0,
2479 mask_top => 0,
2480 @_
2481 );
2482
2483 unless ($self->{IMG}) {
2484 $self->_set_error("compose: empty input image");
2485 return;
2486 }
2487
2488 unless ($opts{src}) {
2489 $self->_set_error("compose: src parameter missing");
2490 return;
2491 }
2492
2493 unless ($opts{src}{IMG}) {
2494 $self->_set_error("compose: src parameter empty image");
2495 return;
2496 }
2497 my $src = $opts{src};
2498
2499 my $left = $opts{left};
2500 defined $left or $left = $opts{tx};
2501 defined $left or $left = 0;
2502
2503 my $top = $opts{top};
2504 defined $top or $top = $opts{ty};
2505 defined $top or $top = 0;
2506
2507 my $src_left = $opts{src_left};
2508 defined $src_left or $src_left = $opts{src_minx};
2509 defined $src_left or $src_left = 0;
2510
2511 my $src_top = $opts{src_top};
2512 defined $src_top or $src_top = $opts{src_miny};
2513 defined $src_top or $src_top = 0;
2514
2515 my $width = $opts{width};
2516 if (!defined $width && defined $opts{src_maxx}) {
2517 $width = $opts{src_maxx} - $src_left;
2518 }
2519 defined $width or $width = $src->getwidth() - $src_left;
2520
2521 my $height = $opts{height};
2522 if (!defined $height && defined $opts{src_maxy}) {
2523 $height = $opts{src_maxy} - $src_top;
2524 }
2525 defined $height or $height = $src->getheight() - $src_top;
2526
2527 my $combine = $self->_combine($opts{combine}, 'normal');
2528
2529 if ($opts{mask}) {
2530 unless ($opts{mask}{IMG}) {
2531 $self->_set_error("compose: mask parameter empty image");
2532 return;
2533 }
2534
2535 my $mask_left = $opts{mask_left};
2536 defined $mask_left or $mask_left = $opts{mask_minx};
2537 defined $mask_left or $mask_left = 0;
2538
2539 my $mask_top = $opts{mask_top};
2540 defined $mask_top or $mask_top = $opts{mask_miny};
2541 defined $mask_top or $mask_top = 0;
2542
618a3282 2543 unless (i_compose_mask($self->{IMG}, $src->{IMG}, $opts{mask}{IMG},
9b1ec2b8
TC
2544 $left, $top, $src_left, $src_top,
2545 $mask_left, $mask_top, $width, $height,
618a3282
TC
2546 $combine, $opts{opacity})) {
2547 $self->_set_error(Imager->_error_as_msg);
2548 return;
2549 }
9b1ec2b8
TC
2550 }
2551 else {
618a3282
TC
2552 unless (i_compose($self->{IMG}, $src->{IMG}, $left, $top, $src_left, $src_top,
2553 $width, $height, $combine, $opts{opacity})) {
2554 $self->_set_error(Imager->_error_as_msg);
2555 return;
2556 }
9b1ec2b8
TC
2557 }
2558
2559 return $self;
2560}
02d1d628 2561
142c26ff
AMH
2562sub flip {
2563 my $self = shift;
2564 my %opts = @_;
9191e525 2565 my %xlate = (h=>0, v=>1, hv=>2, vh=>2);
142c26ff
AMH
2566 my $dir;
2567 return () unless defined $opts{'dir'} and defined $xlate{$opts{'dir'}};
2568 $dir = $xlate{$opts{'dir'}};
2569 return $self if i_flipxy($self->{IMG}, $dir);
2570 return ();
2571}
2572
faa9b3e7
TC
2573sub rotate {
2574 my $self = shift;
2575 my %opts = @_;
34b3f7e6
TC
2576
2577 unless (defined wantarray) {
2578 my @caller = caller;
2579 warn "rotate() called in void context - rotate() returns the rotated image at $caller[1] line $caller[2]\n";
2580 return;
2581 }
2582
faa9b3e7
TC
2583 if (defined $opts{right}) {
2584 my $degrees = $opts{right};
2585 if ($degrees < 0) {
2586 $degrees += 360 * int(((-$degrees)+360)/360);
2587 }
2588 $degrees = $degrees % 360;
2589 if ($degrees == 0) {
2590 return $self->copy();
2591 }
2592 elsif ($degrees == 90 || $degrees == 180 || $degrees == 270) {
2593 my $result = Imager->new();
2594 if ($result->{IMG} = i_rotate90($self->{IMG}, $degrees)) {
2595 return $result;
2596 }
2597 else {
2598 $self->{ERRSTR} = $self->_error_as_msg();
2599 return undef;
2600 }
2601 }
2602 else {
2603 $self->{ERRSTR} = "Parameter 'right' must be a multiple of 90 degrees";
2604 return undef;
2605 }
2606 }
2607 elsif (defined $opts{radians} || defined $opts{degrees}) {
2608 my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
2609
7f627571 2610 my $back = $opts{back};
faa9b3e7 2611 my $result = Imager->new;
7f627571
TC
2612 if ($back) {
2613 $back = _color($back);
2614 unless ($back) {
2615 $self->_set_error(Imager->errstr);
2616 return undef;
2617 }
2618
2619 $result->{IMG} = i_rotate_exact($self->{IMG}, $amount, $back);
0d3b936e
TC
2620 }
2621 else {
2622 $result->{IMG} = i_rotate_exact($self->{IMG}, $amount);
2623 }
2624 if ($result->{IMG}) {
faa9b3e7
TC
2625 return $result;
2626 }
2627 else {
2628 $self->{ERRSTR} = $self->_error_as_msg();
2629 return undef;
2630 }
2631 }
2632 else {
0d3b936e 2633 $self->{ERRSTR} = "Only the 'right', 'radians' and 'degrees' parameters are available";
faa9b3e7
TC
2634 return undef;
2635 }
2636}
2637
2638sub matrix_transform {
2639 my $self = shift;
2640 my %opts = @_;
2641
34b3f7e6
TC
2642 unless (defined wantarray) {
2643 my @caller = caller;
2644 warn "copy() called in void context - copy() returns the copied image at $caller[1] line $caller[2]\n";
2645 return;
2646 }
2647
faa9b3e7
TC
2648 if ($opts{matrix}) {
2649 my $xsize = $opts{xsize} || $self->getwidth;
2650 my $ysize = $opts{ysize} || $self->getheight;
142c26ff 2651
faa9b3e7 2652 my $result = Imager->new;
0d3b936e
TC
2653 if ($opts{back}) {
2654 $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
2655 $opts{matrix}, $opts{back})
2656 or return undef;
2657 }
2658 else {
2659 $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
2660 $opts{matrix})
2661 or return undef;
2662 }
faa9b3e7
TC
2663
2664 return $result;
2665 }
2666 else {
2667 $self->{ERRSTR} = "matrix parameter required";
2668 return undef;
2669 }
2670}
2671
2672# blame Leolo :)
2673*yatf = \&matrix_transform;
02d1d628
AMH
2674
2675# These two are supported for legacy code only
2676
2677sub i_color_new {
faa9b3e7 2678 return Imager::Color->new(@_);
02d1d628
AMH
2679}
2680
2681sub i_color_set {
faa9b3e7 2682 return Imager::Color::set(@_);
02d1d628
AMH
2683}
2684
02d1d628 2685# Draws a box between the specified corner points.
02d1d628
AMH
2686sub box {
2687 my $self=shift;
3b000586
TC
2688 my $raw = $self->{IMG};
2689
2690 unless ($raw) {
2691 $self->{ERRSTR}='empty input image';
2692 return undef;
2693 }
2694
2695 my %opts = @_;
02d1d628 2696
3b000586 2697 my ($xmin, $ymin, $xmax, $ymax);
02d1d628 2698 if (exists $opts{'box'}) {
3b000586
TC
2699 $xmin = _min($opts{'box'}->[0],$opts{'box'}->[2]);
2700 $xmax = _max($opts{'box'}->[0],$opts{'box'}->[2]);
2701 $ymin = _min($opts{'box'}->[1],$opts{'box'}->[3]);
2702 $ymax = _max($opts{'box'}->[1],$opts{'box'}->[3]);
2703 }
2704 else {
2705 defined($xmin = $opts{xmin}) or $xmin = 0;
2706 defined($xmax = $opts{xmax}) or $xmax = $self->getwidth()-1;
2707 defined($ymin = $opts{ymin}) or $ymin = 0;
2708 defined($ymax = $opts{ymax}) or $ymax = $self->getheight()-1;
02d1d628
AMH
2709 }
2710
f1ac5027 2711 if ($opts{filled}) {
4dd88895
TC
2712 my $color = $opts{'color'};
2713
2714 if (defined $color) {
813d4d0a 2715 unless (_is_color_object($color)) {
4dd88895
TC
2716 $color = _color($color);
2717 unless ($color) {
2718 $self->{ERRSTR} = $Imager::ERRSTR;
2719 return;
2720 }
2721 }
3a9a4241 2722 }
4dd88895
TC
2723 else {
2724 $color = i_color_new(255,255,255,255);
2725 }
2726
7477ff14
TC
2727 if ($color->isa("Imager::Color")) {
2728 i_box_filled($raw, $xmin, $ymin,$xmax, $ymax, $color);
2729 }
2730 else {
2731 i_box_filledf($raw, $xmin, $ymin,$xmax, $ymax, $color);
2732 }
f1ac5027
TC
2733 }
2734 elsif ($opts{fill}) {
2735 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2736 # assume it's a hash ref
2737 require 'Imager/Fill.pm';
141a6114
TC
2738 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2739 $self->{ERRSTR} = $Imager::ERRSTR;
2740 return undef;
2741 }
f1ac5027 2742 }
3b000586 2743 i_box_cfill($raw, $xmin, $ymin, $xmax, $ymax, $opts{fill}{fill});
f1ac5027 2744 }
cdd23610 2745 else {
4dd88895
TC
2746 my $color = $opts{'color'};
2747 if (defined $color) {
813d4d0a 2748 unless (_is_color_object($color)) {
4dd88895
TC
2749 $color = _color($color);
2750 unless ($color) {
2751 $self->{ERRSTR} = $Imager::ERRSTR;
2752 return;
2753 }
2754 }
2755 }
2756 else {
2757 $color = i_color_new(255, 255, 255, 255);
2758 }
3a9a4241 2759 unless ($color) {
cdd23610
AMH
2760 $self->{ERRSTR} = $Imager::ERRSTR;
2761 return;
3a9a4241 2762 }
3b000586 2763 i_box($raw, $xmin, $ymin, $xmax, $ymax, $color);
f1ac5027 2764 }
3b000586 2765
02d1d628
AMH
2766 return $self;
2767}
2768
02d1d628
AMH
2769sub arc {
2770 my $self=shift;
2771 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
40068b33
TC
2772 my $dflcl= [ 255, 255, 255, 255];
2773 my $good = 1;
2774 my %opts=
2775 (
2776 color=>$dflcl,
2777 'r'=>_min($self->getwidth(),$self->getheight())/3,
2778 'x'=>$self->getwidth()/2,
2779 'y'=>$self->getheight()/2,
2780 'd1'=>0, 'd2'=>361,
2781 filled => 1,
2782 @_,
2783 );
a8652edf
TC
2784 if ($opts{aa}) {
2785 if ($opts{fill}) {
2786 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2787 # assume it's a hash ref
2788 require 'Imager/Fill.pm';
2789 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2790 $self->{ERRSTR} = $Imager::ERRSTR;
2791 return;
2792 }
2793 }
2794 i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
2795 $opts{'d2'}, $opts{fill}{fill});
2796 }
40068b33 2797 elsif ($opts{filled}) {
a8652edf
TC
2798 my $color = _color($opts{'color'});
2799 unless ($color) {
2800 $self->{ERRSTR} = $Imager::ERRSTR;
2801 return;
2802 }
2803 if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
2804 i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
2805 $color);
2806 }
2807 else {
2808 i_arc_aa($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
2809 $opts{'d1'}, $opts{'d2'}, $color);
569795e8 2810 }
f1ac5027 2811 }
40068b33
TC
2812 else {
2813 my $color = _color($opts{'color'});
2814 if ($opts{d2} - $opts{d1} >= 360) {
2815 $good = i_circle_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $color);
2816 }
2817 else {
2818 $good = i_arc_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $opts{'d1'}, $opts{'d2'}, $color);
2819 }
2820 }
f1ac5027
TC
2821 }
2822 else {
a8652edf
TC
2823 if ($opts{fill}) {
2824 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
2825 # assume it's a hash ref
2826 require 'Imager/Fill.pm';
2827 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
2828 $self->{ERRSTR} = $Imager::ERRSTR;
2829 return;
2830 }
2831 }
2832 i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
2833 $opts{'d2'}, $opts{fill}{fill});
0d321238
TC
2834 }
2835 else {
a8652edf
TC
2836 my $color = _color($opts{'color'});
2837 unless ($color) {
2838 $self->{ERRSTR} = $Imager::ERRSTR;
40068b33
TC
2839 return;
2840 }
2841 if ($opts{filled}) {
2842 i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
2843 $opts{'d1'}, $opts{'d2'}, $color);
2844 }
2845 else {
2846 if ($opts{d1} == 0 && $opts{d2} == 361) {
2847 $good = i_circle_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $color);
2848 }
2849 else {
2850 $good = i_arc_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $opts{d1}, $opts{d2}, $color);
2851 }
a8652edf 2852 }
0d321238 2853 }
f1ac5027 2854 }
40068b33
TC
2855 unless ($good) {
2856 $self->_set_error($self->_error_as_msg);
2857 return;
2858 }
f1ac5027 2859
02d1d628
AMH
2860 return $self;
2861}
2862
aa833c97
AMH
2863# Draws a line from one point to the other
2864# the endpoint is set if the endp parameter is set which it is by default.
2865# to turn of the endpoint being set use endp=>0 when calling line.
02d1d628
AMH
2866
2867sub line {
2868 my $self=shift;
2869 my $dflcl=i_color_new(0,0,0,0);
aa833c97
AMH
2870 my %opts=(color=>$dflcl,
2871 endp => 1,
2872 @_);
02d1d628
AMH
2873 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2874
2875 unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; }
2876 unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; }
2877
3a9a4241 2878 my $color = _color($opts{'color'});
aa833c97
AMH
2879 unless ($color) {
2880 $self->{ERRSTR} = $Imager::ERRSTR;
2881 return;
3a9a4241 2882 }
aa833c97 2883
3a9a4241 2884 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628 2885 if ($opts{antialias}) {
aa833c97 2886 i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
b437ce0a 2887 $color, $opts{endp});
02d1d628 2888 } else {
aa833c97
AMH
2889 i_line($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2},
2890 $color, $opts{endp});
02d1d628
AMH
2891 }
2892 return $self;
2893}
2894
2895# Draws a line between an ordered set of points - It more or less just transforms this
2896# into a list of lines.
2897
2898sub polyline {
2899 my $self=shift;
2900 my ($pt,$ls,@points);
2901 my $dflcl=i_color_new(0,0,0,0);
2902 my %opts=(color=>$dflcl,@_);
2903
2904 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2905
2906 if (exists($opts{points})) { @points=@{$opts{points}}; }
2907 if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) {
2908 @points=map { [ $opts{'x'}->[$_],$opts{'y'}->[$_] ] } (0..(scalar @{$opts{'x'}}-1));
2909 }
2910
2911# print Dumper(\@points);
2912
3a9a4241
TC
2913 my $color = _color($opts{'color'});
2914 unless ($color) {
2915 $self->{ERRSTR} = $Imager::ERRSTR;
2916 return;
2917 }
2918 $opts{antialias} = $opts{aa} if defined $opts{aa};
02d1d628
AMH
2919 if ($opts{antialias}) {
2920 for $pt(@points) {
3a9a4241 2921 if (defined($ls)) {
b437ce0a 2922 i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color, 1);
3a9a4241 2923 }
02d1d628
AMH
2924 $ls=$pt;
2925 }
2926 } else {
2927 for $pt(@points) {
3a9a4241 2928 if (defined($ls)) {
aa833c97 2929 i_line($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color,1);
3a9a4241 2930 }
02d1d628
AMH
2931 $ls=$pt;
2932 }
2933 }
2934 return $self;
2935}
2936
d0e7bfee
AMH
2937sub polygon {
2938 my $self = shift;
2939 my ($pt,$ls,@points);
2940 my $dflcl = i_color_new(0,0,0,0);
2941 my %opts = (color=>$dflcl, @_);
2942
2943 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2944
2945 if (exists($opts{points})) {
2946 $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ];
2947 $opts{'y'} = [ map { $_->[1] } @{$opts{points}} ];
2948 }
2949
2950 if (!exists $opts{'x'} or !exists $opts{'y'}) {
2951 $self->{ERRSTR} = 'no points array, or x and y arrays.'; return undef;
2952 }
2953
43c5dacb
TC
2954 if ($opts{'fill'}) {
2955 unless (UNIVERSAL::isa($opts{'fill'}, 'Imager::Fill')) {
2956 # assume it's a hash ref
2957 require 'Imager/Fill.pm';
2958 unless ($opts{'fill'} = Imager::Fill->new(%{$opts{'fill'}})) {
2959 $self->{ERRSTR} = $Imager::ERRSTR;
2960 return undef;
2961 }
2962 }
2963 i_poly_aa_cfill($self->{IMG}, $opts{'x'}, $opts{'y'},
2964 $opts{'fill'}{'fill'});
2965 }
2966 else {
3a9a4241
TC
2967 my $color = _color($opts{'color'});
2968 unless ($color) {
2969 $self->{ERRSTR} = $Imager::ERRSTR;
2970 return;
2971 }
2972 i_poly_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
43c5dacb
TC
2973 }
2974
d0e7bfee
AMH
2975 return $self;
2976}
2977
2978
2979# this the multipoint bezier curve
02d1d628
AMH
2980# this is here more for testing that actual usage since
2981# this is not a good algorithm. Usually the curve would be
2982# broken into smaller segments and each done individually.
2983
2984sub polybezier {
2985 my $self=shift;
2986 my ($pt,$ls,@points);
2987 my $dflcl=i_color_new(0,0,0,0);
2988 my %opts=(color=>$dflcl,@_);
2989
2990 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
2991
2992 if (exists $opts{points}) {
2993 $opts{'x'}=map { $_->[0]; } @{$opts{'points'}};
2994 $opts{'y'}=map { $_->[1]; } @{$opts{'points'}};
2995 }
2996
2997 unless ( @{$opts{'x'}} and @{$opts{'x'}} == @{$opts{'y'}} ) {
2998 $self->{ERRSTR}='Missing or invalid points.';
2999 return;
3000 }
3001
3a9a4241
TC
3002 my $color = _color($opts{'color'});
3003 unless ($color) {
3004 $self->{ERRSTR} = $Imager::ERRSTR;
3005 return;
3006 }
3007 i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$color);
02d1d628
AMH
3008 return $self;
3009}
3010
cc6483e0
TC
3011sub flood_fill {
3012 my $self = shift;
3013 my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
aa833c97
AMH
3014 my $rc;
3015
9d540150 3016 unless (exists $opts{'x'} && exists $opts{'y'}) {
cc6483e0
TC
3017 $self->{ERRSTR} = "missing seed x and y parameters";
3018 return undef;
3019 }
07d70837 3020
3efb0915
TC
3021 if ($opts{border}) {
3022 my $border = _color($opts{border});
3023 unless ($border) {
3024 $self->_set_error($Imager::ERRSTR);
3025 return;
3026 }
3027 if ($opts{fill}) {
3028 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
3029 # assume it's a hash ref
3030 require Imager::Fill;
3031 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
3032 $self->{ERRSTR} = $Imager::ERRSTR;
3033 return;
3034 }
569795e8 3035 }
3efb0915
TC
3036 $rc = i_flood_cfill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
3037 $opts{fill}{fill}, $border);
3038 }
3039 else {
3040 my $color = _color($opts{'color'});
3041 unless ($color) {
3042 $self->{ERRSTR} = $Imager::ERRSTR;
3043 return;
3044 }
3045 $rc = i_flood_fill_border($self->{IMG}, $opts{'x'}, $opts{'y'},
3046 $color, $border);
3047 }
3048 if ($rc) {
3049 return $self;
3050 }
3051 else {
3052 $self->{ERRSTR} = $self->_error_as_msg();
3053 return;
cc6483e0 3054 }
cc6483e0
TC
3055 }
3056 else {
3efb0915
TC
3057 if ($opts{fill}) {
3058 unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
3059 # assume it's a hash ref
3060 require 'Imager/Fill.pm';
3061 unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
3062 $self->{ERRSTR} = $Imager::ERRSTR;
3063 return;
3064 }
3065 }
3066 $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
3067 }
3068 else {
3069 my $color = _color($opts{'color'});
3070 unless ($color) {
3071 $self->{ERRSTR} = $Imager::ERRSTR;
3072 return;
3073 }
3074 $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
3075 }
3076 if ($rc) {
3077 return $self;
3078 }
3079 else {
3080 $self->{ERRSTR} = $self->_error_as_msg();
aa833c97 3081 return;
3a9a4241 3082 }
3efb0915 3083 }
cc6483e0
TC
3084}
3085
591b5954 3086sub setpixel {
b3cdc973 3087 my ($self, %opts) = @_;
591b5954 3088
b3cdc973
TC
3089 my $color = $opts{color};
3090 unless (defined $color) {
3091 $color = $self->{fg};
3092 defined $color or $color = NC(255, 255, 255);
3093 }
3094
3095 unless (ref $color && UNIVERSAL::isa($color, "Imager::Color")) {
3096 $color = _color($color)
3097 or return undef;
3098 }
591b5954
TC
3099
3100 unless (exists $opts{'x'} && exists $opts{'y'}) {
3101 $self->{ERRSTR} = 'missing x and y parameters';
3102 return undef;
3103 }
3104
3105 my $x = $opts{'x'};
3106 my $y = $opts{'y'};
591b5954
TC
3107 if (ref $x && ref $y) {
3108 unless (@$x == @$y) {
9650c424 3109 $self->{ERRSTR} = 'length of x and y mismatch';
837a4b43 3110 return;
591b5954 3111 }
837a4b43 3112 my $set = 0;
591b5954
TC
3113 if ($color->isa('Imager::Color')) {
3114 for my $i (0..$#{$opts{'x'}}) {
837a4b43
TC
3115 i_ppix($self->{IMG}, $x->[$i], $y->[$i], $color)
3116 or ++$set;
591b5954
TC
3117 }
3118 }
3119 else {
3120 for my $i (0..$#{$opts{'x'}}) {
837a4b43
TC
3121 i_ppixf($self->{IMG}, $x->[$i], $y->[$i], $color)
3122 or ++$set;
591b5954
TC
3123 }
3124 }
837a4b43
TC
3125 $set or return;
3126 return $set;
591b5954
TC
3127 }
3128 else {
3129 if ($color->isa('Imager::Color')) {
837a4b43
TC
3130 i_ppix($self->{IMG}, $x, $y, $color)
3131 and return;
591b5954
TC
3132 }
3133 else {
837a4b43
TC
3134 i_ppixf($self->{IMG}, $x, $y, $color)
3135 and return;
591b5954
TC
3136 }
3137 }
3138
3139 $self;
3140}
3141
3142sub getpixel {
3143 my $self = shift;
3144
a9fa203f 3145 my %opts = ( "type"=>'8bit', @_);
591b5954
TC
3146
3147 unless (exists $opts{'x'} && exists $opts{'y'}) {
3148 $self->{ERRSTR} = 'missing x and y parameters';
3149 return undef;
3150 }
3151
3152 my $x = $opts{'x'};
3153 my $y = $opts{'y'};
3154 if (ref $x && ref $y) {
3155 unless (@$x == @$y) {
3156 $self->{ERRSTR} = 'length of x and y mismatch';
3157 return undef;
3158 }
3159 my @result;
a9fa203f 3160 if ($opts{"type"} eq '8bit') {
591b5954
TC
3161 for my $i (0..$#{$opts{'x'}}) {
3162 push(@result, i_get_pixel($self->{IMG}, $x->[$i], $y->[$i]));
3163 }
3164 }
3165 else {
3166 for my $i (0..$#{$opts{'x'}}) {
3167 push(@result, i_gpixf($self->{IMG}, $x->[$i], $y->[$i]));
3168 }
3169 }
3170 return wantarray ? @result : \@result;
3171 }
3172 else {
a9fa203f 3173 if ($opts{"type"} eq '8bit') {
591b5954
TC
3174 return i_get_pixel($self->{IMG}, $x, $y);
3175 }
3176 else {
3177 return i_gpixf($self->{IMG}, $x, $y);
3178 }
3179 }
3180
3181 $self;
3182}
3183
ca4d914e
TC
3184sub getscanline {
3185 my $self = shift;
3186 my %opts = ( type => '8bit', x=>0, @_);
3187
4cda4e76
TC
3188 $self->_valid_image or return;
3189
ca4d914e
TC
3190 defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
3191
3192 unless (defined $opts{'y'}) {
3193 $self->_set_error("missing y parameter");
3194 return;
3195 }
3196
3197 if ($opts{type} eq '8bit') {
3198 return i_glin($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
4cda4e76 3199 $opts{'y'});
ca4d914e
TC
3200 }
3201 elsif ($opts{type} eq 'float') {
3202 return i_glinf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
4cda4e76
TC
3203 $opts{'y'});
3204 }
3205 elsif ($opts{type} eq 'index') {
3206 unless (i_img_type($self->{IMG})) {
3207 $self->_set_error("type => index only valid on paletted images");
3208 return;
3209 }
3210 return i_gpal($self->{IMG}, $opts{x}, $opts{x} + $opts{width},
3211 $opts{'y'});
ca4d914e
TC
3212 }
3213 else {
3214 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3215 return;
3216 }
3217}
3218
3219sub setscanline {
3220 my $self = shift;
3221 my %opts = ( x=>0, @_);
3222
4cda4e76
TC
3223 $self->_valid_image or return;
3224
ca4d914e
TC
3225 unless (defined $opts{'y'}) {
3226 $self->_set_error("missing y parameter");
3227 return;
3228 }
3229
3230 if (!$opts{type}) {
3231 if (ref $opts{pixels} && @{$opts{pixels}}) {
3232 # try to guess the type
3233 if ($opts{pixels}[0]->isa('Imager::Color')) {
3234 $opts{type} = '8bit';
3235 }
3236 elsif ($opts{pixels}[0]->isa('Imager::Color::Float')) {
3237 $opts{type} = 'float';
3238 }
3239 else {
3240 $self->_set_error("missing type parameter and could not guess from pixels");
3241 return;
3242 }
3243 }
3244 else {
3245 # default
3246 $opts{type} = '8bit';
3247 }
3248 }
3249
3250 if ($opts{type} eq '8bit') {
3251 if (ref $opts{pixels}) {
3252 return i_plin($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3253 }
3254 else {
3255 return i_plin($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3256 }
3257 }
3258 elsif ($opts{type} eq 'float') {
3259 if (ref $opts{pixels}) {
3260 return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3261 }
3262 else {
3263 return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3264 }
3265 }
4cda4e76
TC
3266 elsif ($opts{type} eq 'index') {
3267 if (ref $opts{pixels}) {
3268 return i_ppal($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
3269 }
3270 else {
3271 return i_ppal_p($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
3272 }
3273 }
ca4d914e
TC
3274 else {
3275 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3276 return;
3277 }
3278}
3279
3280sub getsamples {
3281 my $self = shift;
bd8052a6 3282 my %opts = ( type => '8bit', x=>0, offset => 0, @_);
ca4d914e
TC
3283
3284 defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
3285
3286 unless (defined $opts{'y'}) {
3287 $self->_set_error("missing y parameter");
3288 return;
3289 }
3290
3291 unless ($opts{channels}) {
3292 $opts{channels} = [ 0 .. $self->getchannels()-1 ];
3293 }
3294
bd8052a6
TC
3295 if ($opts{target}) {
3296 my $target = $opts{target};
3297 my $offset = $opts{offset};
3298 if ($opts{type} eq '8bit') {
3299 my @samples = i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3300 $opts{y}, @{$opts{channels}})
3301 or return;
3302 @{$target}{$offset .. $offset + @samples - 1} = @samples;
3303 return scalar(@samples);
3304 }
3305 elsif ($opts{type} eq 'float') {
3306 my @samples = i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3307 $opts{y}, @{$opts{channels}});
3308 @{$target}{$offset .. $offset + @samples - 1} = @samples;
3309 return scalar(@samples);
3310 }
3311 elsif ($opts{type} =~ /^(\d+)bit$/) {
3312 my $bits = $1;
3313
3314 my @data;
3315 my $count = i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3316 $opts{y}, $bits, $target,
3317 $offset, @{$opts{channels}});
3318 unless (defined $count) {
3319 $self->_set_error(Imager->_error_as_msg);
3320 return;
3321 }
3322
3323 return $count;
3324 }
3325 else {
3326 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3327 return;
3328 }
ca4d914e
TC
3329 }
3330 else {
bd8052a6
TC
3331 if ($opts{type} eq '8bit') {
3332 return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3333 $opts{y}, @{$opts{channels}});
3334 }
3335 elsif ($opts{type} eq 'float') {
3336 return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3337 $opts{y}, @{$opts{channels}});
3338 }
3339 elsif ($opts{type} =~ /^(\d+)bit$/) {
3340 my $bits = $1;
3341
3342 my @data;
3343 i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
3344 $opts{y}, $bits, \@data, 0, @{$opts{channels}})
3345 or return;
3346 return @data;
3347 }
3348 else {
3349 $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
3350 return;
3351 }
3352 }
3353}
3354
3355sub setsamples {
3356 my $self = shift;
3357 my %opts = ( x => 0, offset => 0, @_ );
3358
3359 unless ($self->{IMG}) {
3360 $self->_set_error('setsamples: empty input image');
3361 return;
3362 }
3363
3364 unless(defined $opts{data} && ref $opts{data}) {
3365 $self->_set_error('setsamples: data parameter missing or invalid');
ca4d914e
TC
3366 return;
3367 }
bd8052a6
TC
3368
3369 unless ($opts{channels}) {
3370 $opts{channels} = [ 0 .. $self->getchannels()-1 ];
3371 }
3372
3373 unless ($opts{type} && $opts{type} =~ /^(\d+)bit$/) {
3374 $self->_set_error('setsamples: type parameter missing or invalid');
3375 return;
3376 }
3377 my $bits = $1;
3378
3379 unless (defined $opts{width}) {
3380 $opts{width} = $self->getwidth() - $opts{x};
3381 }
3382
3383 my $count = i_psamp_bits($self->{IMG}, $opts{x}, $opts{y}, $bits,
3384 $opts{channels}, $opts{data}, $opts{offset},
3385 $opts{width});
3386 unless (defined $count) {
3387 $self->_set_error(Imager->_error_as_msg);
3388 return;
3389 }
3390
3391 return $count;
ca4d914e
TC
3392}
3393
f5991c03
TC
3394# make an identity matrix of the given size
3395sub _identity {
3396 my ($size) = @_;
3397
3398 my $matrix = [ map { [ (0) x $size ] } 1..$size ];
3399 for my $c (0 .. ($size-1)) {
3400 $matrix->[$c][$c] = 1;
3401 }
3402 return $matrix;
3403}
3404
3405# general function to convert an image
3406sub convert {
3407 my ($self, %opts) = @_;
3408 my $matrix;
3409
34b3f7e6
TC
3410 unless (defined wantarray) {
3411 my @caller = caller;
3412 warn "convert() called in void context - convert() returns the converted image at $caller[1] line $caller[2]\n";
3413 return;
3414 }
3415
f5991c03
TC
3416 # the user can either specify a matrix or preset
3417 # the matrix overrides the preset
3418 if (!exists($opts{matrix})) {
3419 unless (exists($opts{preset})) {
3420 $self->{ERRSTR} = "convert() needs a matrix or preset";
3421 return;
3422 }
3423 else {
3424 if ($opts{preset} eq 'gray' || $opts{preset} eq 'grey') {
3425 # convert to greyscale, keeping the alpha channel if any
3426 if ($self->getchannels == 3) {
3427 $matrix = [ [ 0.222, 0.707, 0.071 ] ];
3428 }
3429 elsif ($self->getchannels == 4) {
3430 # preserve the alpha channel
3431 $matrix = [ [ 0.222, 0.707, 0.071, 0 ],
3432 [ 0, 0, 0, 1 ] ];
3433 }
3434 else {
3435 # an identity
3436 $matrix = _identity($self->getchannels);
3437 }
3438 }
3439 elsif ($opts{preset} eq 'noalpha') {
3440 # strip the alpha channel
3441 if ($self->getchannels == 2 or $self->getchannels == 4) {
3442 $matrix = _identity($self->getchannels);
3443 pop(@$matrix); # lose the alpha entry
3444 }
3445 else {
3446 $matrix = _identity($self->getchannels);
3447 }
3448 }
3449 elsif ($opts{preset} eq 'red' || $opts{preset} eq 'channel0') {
3450 # extract channel 0
3451 $matrix = [ [ 1 ] ];
3452 }
3453 elsif ($opts{preset} eq 'green' || $opts{preset} eq 'channel1') {
3454 $matrix = [ [ 0, 1 ] ];
3455 }
3456 elsif ($opts{preset} eq 'blue' || $opts{preset} eq 'channel2') {
3457 $matrix = [ [ 0, 0, 1 ] ];
3458 }
3459 elsif ($opts{preset} eq 'alpha') {
3460 if ($self->getchannels == 2 or $self->getchannels == 4) {
3461 $matrix = [ [ (0) x ($self->getchannels-1), 1 ] ];
3462 }
3463 else {
3464 # the alpha is just 1 <shrug>
3465 $matrix = [ [ (0) x $self->getchannels, 1 ] ];
3466 }
3467 }
3468 elsif ($opts{preset} eq 'rgb') {
3469 if ($self->getchannels == 1) {
3470 $matrix = [ [ 1 ], [ 1 ], [ 1 ] ];
3471 }
3472 elsif ($self->getchannels == 2) {
3473 # preserve the alpha channel
3474 $matrix = [ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ], [ 0, 1 ] ];
3475 }
3476 else {
3477 $matrix = _identity($self->getchannels);
3478 }
3479 }
3480 elsif ($opts{preset} eq 'addalpha') {
3481 if ($self->getchannels == 1) {
3482 $matrix = _identity(2);
3483 }
3484 elsif ($self->getchannels == 3) {
3485 $matrix = _identity(4);
3486 }
3487 else {
3488 $matrix = _identity($self->getchannels);
3489 }
3490 }
3491 else {
3492 $self->{ERRSTR} = "Unknown convert preset $opts{preset}";
3493 return undef;
3494 }
3495 }
3496 }
3497 else {
3498 $matrix = $opts{matrix};
3499 }
3500
d5477d3d
TC
3501 my $new = Imager->new;
3502 $new->{IMG} = i_convert($self->{IMG}, $matrix);
3503 unless ($new->{IMG}) {
f5991c03
TC
3504 # most likely a bad matrix
3505 $self->{ERRSTR} = _error_as_msg();
3506 return undef;
3507 }
3508 return $new;
3509}
40eba1ea 3510
b47464c1
TC
3511# combine channels from multiple input images, a class method
3512sub combine {
3513 my ($class, %opts) = @_;
3514
3515 my $src = delete $opts{src};
3516 unless ($src) {
3517 $class->_set_error("src parameter missing");
3518 return;
3519 }
3520 my @imgs;
3521 my $index = 0;
3522 for my $img (@$src) {
3523 unless (eval { $img->isa("Imager") }) {
3524 $class->_set_error("src must contain image objects");
3525 return;
3526 }
3527 unless ($img->{IMG}) {
3528 $class->_set_error("empty input image");
3529 return;
3530 }
3531 push @imgs, $img->{IMG};
3532 }
3533 my $result;
3534 if (my $channels = delete $opts{channels}) {
3535 $result = i_combine(\@imgs, $channels);
3536 }
3537 else {
3538 $result = i_combine(\@imgs);
3539 }
3540 unless ($result) {
3541 $class->_set_error($class->_error_as_msg);
3542 return;
3543 }
3544
3545 my $img = $class->new;
3546 $img->{IMG} = $result;
3547
3548 return $img;
3549}
3550
40eba1ea 3551
40eba1ea 3552# general function to map an image through lookup tables
9495ee93 3553
40eba1ea
AMH
3554sub map {
3555 my ($self, %opts) = @_;
9495ee93 3556 my @chlist = qw( red green blue alpha );
40eba1ea
AMH
3557
3558 if (!exists($opts{'maps'})) {
3559 # make maps from channel maps
3560 my $chnum;
3561 for $chnum (0..$#chlist) {
9495ee93
AMH
3562 if (exists $opts{$chlist[$chnum]}) {
3563 $opts{'maps'}[$chnum] = $opts{$chlist[$chnum]};
3564 } elsif (exists $opts{'all'}) {
3565 $opts{'maps'}[$chnum] = $opts{'all'};
3566 }
40eba1ea
AMH
3567 }
3568 }
3569 if ($opts{'maps'} and $self->{IMG}) {
3570 i_map($self->{IMG}, $opts{'maps'} );
3571 }
3572 return $self;
3573}
3574
dff75dee
TC
3575sub difference {
3576 my ($self, %opts) = @_;
3577
3578 defined $opts{mindist} or $opts{mindist} = 0;
3579
3580 defined $opts{other}
3581 or return $self->_set_error("No 'other' parameter supplied");
3582 defined $opts{other}{IMG}
3583 or return $self->_set_error("No image data in 'other' image");
3584
3585 $self->{IMG}
3586 or return $self->_set_error("No image data");
3587
3588 my $result = Imager->new;
3589 $result->{IMG} = i_diff_image($self->{IMG}, $opts{other}{IMG},
3590 $opts{mindist})
3591 or return $self->_set_error($self->_error_as_msg());
3592
3593 return $result;
3594}
3595
02d1d628
AMH
3596# destructive border - image is shrunk by one pixel all around
3597
3598sub border {
3599 my ($self,%opts)=@_;
3600 my($tx,$ty)=($self->getwidth()-1,$self->getheight()-1);
3601 $self->polyline('x'=>[0,$tx,$tx,0,0],'y'=>[0,0,$ty,$ty,0],%opts);
3602}
3603
3604
3605# Get the width of an image
3606
3607sub getwidth {
3608 my $self = shift;
3b000586
TC
3609
3610 if (my $raw = $self->{IMG}) {
3611 return i_img_get_width($raw);
3612 }
3613 else {
3614 $self->{ERRSTR} = 'image is empty'; return undef;
3615 }
02d1d628
AMH
3616}
3617
3618# Get the height of an image
3619
3620sub getheight {
3621 my $self = shift;
3b000586
TC
3622
3623 if (my $raw = $self->{IMG}) {
3624 return i_img_get_height($raw);
3625 }
3626 else {
3627 $self->{ERRSTR} = 'image is empty'; return undef;
3628 }
02d1d628
AMH
3629}
3630
3631# Get number of channels in an image
3632
3633sub getchannels {
3634 my $self = shift;
3635 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
3636 return i_img_getchannels($self->{IMG});
3637}
3638
3639# Get channel mask
3640
3641sub getmask {
3642 my $self = shift;
3643 if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
3644 return i_img_getmask($self->{IMG});
3645}
3646
3647# Set channel mask
3648
3649sub setmask {
3650 my $self = shift;
3651 my %opts = @_;
35f40526
TC
3652 if (!defined($self->{IMG})) {
3653 $self->{ERRSTR} = 'image is empty';
3654 return undef;
3655 }
3656 unless (defined $opts{mask}) {
3657 $self->_set_error("mask parameter required");
3658 return;
3659 }
02d1d628 3660 i_img_setmask( $self->{IMG} , $opts{mask} );
35f40526
TC
3661
3662 1;
02d1d628
AMH
3663}
3664
3665# Get number of colors in an image
3666
3667sub getcolorcount {
3668 my $self=shift;
9d540150 3669 my %opts=('maxcolors'=>2**30,@_);
02d1d628
AMH
3670 if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
3671 my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
3672 return ($rc==-1? undef : $rc);
3673}
3674
fe622da1
TC
3675# Returns a reference to a hash. The keys are colour named (packed) and the
3676# values are the number of pixels in this colour.
3677sub getcolorusagehash {
a60905e4
TC
3678 my $self = shift;
3679
3680 my %opts = ( maxcolors => 2**30, @_ );
3681 my $max_colors = $opts{maxcolors};
3682 unless (defined $max_colors && $max_colors > 0) {
3683 $self->_set_error('maxcolors must be a positive integer');
3684 return;
3685 }
3686
3687 unless (defined $self->{IMG}) {
3688 $self->_set_error('empty input image');
3689 return;
3690 }
3691
3692 my $channels= $self->getchannels;
3693 # We don't want to look at the alpha channel, because some gifs using it
3694 # doesn't define it for every colour (but only for some)
3695 $channels -= 1 if $channels == 2 or $channels == 4;
3696 my %color_use;
3697 my $height = $self->getheight;
3698 for my $y (0 .. $height - 1) {
3699 my $colors = $self->getsamples('y' => $y, channels => [ 0 .. $channels - 1 ]);
3700 while (length $colors) {
3701 $color_use{ substr($colors, 0, $channels, '') }++;
fe622da1 3702 }
a60905e4
TC
3703 keys %color_use > $max_colors
3704 and return;
3705 }
3706 return \%color_use;
fe622da1
TC
3707}
3708
3709# This will return a ordered array of the colour usage. Kind of the sorted
3710# version of the values of the hash returned by getcolorusagehash.
3711# You might want to add safety checks and change the names, etc...
3712sub getcolorusage {
a60905e4
TC
3713 my $self = shift;
3714
3715 my %opts = ( maxcolors => 2**30, @_ );
3716 my $max_colors = $opts{maxcolors};
3717 unless (defined $max_colors && $max_colors > 0) {
3718 $self->_set_error('maxcolors must be a positive integer');
3719 return;
3720 }
3721
3722 unless (defined $self->{IMG}) {
3723 $self->_set_error('empty input image');
3724 return undef;
3725 }
3726
3727 return i_get_anonymous_color_histo($self->{IMG}, $max_colors);
fe622da1
TC
3728}
3729
02d1d628
AMH
3730# draw string to an image
3731
3732sub string {
3733 my $self = shift;
3734 unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
3735
3736 my %input=('x'=>0, 'y'=>0, @_);
4314a320 3737 defined($input{string}) or $input{string} = $input{text};
02d1d628 3738
e922ae66 3739 unless(defined $input{string}) {
02d1d628
AMH
3740 $self->{ERRSTR}="missing required parameter 'string'";
3741 return;
3742 }
3743
3744 unless($input{font}) {
3745 $self->{ERRSTR}="missing required parameter 'font'";
3746 return;
3747 }
3748
faa9b3e7 3749 unless ($input{font}->draw(image=>$self, %input)) {
faa9b3e7
TC
3750 return;
3751 }
02d1d628
AMH
3752
3753 return $self;
3754}
3755
a7ccc5e2
TC
3756sub align_string {
3757 my $self = shift;
e922ae66
TC
3758
3759 my $img;
3760 if (ref $self) {
3761 unless ($self->{IMG}) {
3762 $self->{ERRSTR}='empty input image';
3763 return;
3764 }
c9cb3397 3765 $img = $self;
e922ae66
TC
3766 }
3767 else {
3768 $img = undef;
3769 }
a7ccc5e2
TC
3770
3771 my %input=('x'=>0, 'y'=>0, @_);
120f4287
TC
3772 defined $input{string}
3773 or $input{string} = $input{text};
a7ccc5e2
TC
3774
3775 unless(exists $input{string}) {
e922ae66 3776 $self->_set_error("missing required parameter 'string'");
a7ccc5e2
TC
3777 return;
3778 }
3779
3780 unless($input{font}) {
e922ae66 3781 $self->_set_error("missing required parameter 'font'");
a7ccc5e2
TC
3782 return;
3783 }
3784
3785 my @result;
e922ae66 3786 unless (@result = $input{font}->align(image=>$img, %input)) {
a7ccc5e2
TC
3787 return;
3788 }
3789
3790 return wantarray ? @result : $result[0];
3791}
3792
77157728
TC
3793my @file_limit_names = qw/width height bytes/;
3794
3795sub set_file_limits {
3796 shift;
3797
3798 my %opts = @_;
3799 my %values;
3800
3801 if ($opts{reset}) {
3802 @values{@file_limit_names} = (0) x @file_limit_names;
3803 }
3804 else {
3805 @values{@file_limit_names} = i_get_image_file_limits();
3806 }
3807
3808 for my $key (keys %values) {
3809 defined $opts{$key} and $values{$key} = $opts{$key};
3810 }
3811
3812 i_set_image_file_limits($values{width}, $values{height}, $values{bytes});
3813}
3814
3815sub get_file_limits {
3816 i_get_image_file_limits();
3817}
3818
02d1d628
AMH
3819# Shortcuts that can be exported
3820
3821sub newcolor { Imager::Color->new(@_); }
3822sub newfont { Imager::Font->new(@_); }
4697b0b9
TC
3823sub NCF {
3824 require Imager::Color::Float;
3825 return Imager::Color::Float->new(@_);
3826}
02d1d628
AMH
3827
3828*NC=*newcolour=*newcolor;
3829*NF=*newfont;
3830
3831*open=\&read;
3832*circle=\&arc;
3833
3834
3835#### Utility routines
3836
faa9b3e7
TC
3837sub errstr {
3838 ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
3839}
02d1d628 3840
10461f9a
TC
3841sub _set_error {
3842 my ($self, $msg) = @_;
3843
3844 if (ref $self) {
3845 $self->{ERRSTR} = $msg;
3846 }
3847 else {
3848 $ERRSTR = $msg;
3849 }
dff75dee 3850 return;
10461f9a
TC
3851}
3852
02d1d628
AMH
3853# Default guess for the type of an image from extension
3854
3855sub def_guess_type {
3856 my $name=lc(shift);
3857 my $ext;
3858 $ext=($name =~ m/\.([^\.]+)$/)[0];
3859 return 'tiff' if ($ext =~ m/^tiff?$/);
3860 return 'jpeg' if ($ext =~ m/^jpe?g$/);
3861 return 'pnm' if ($ext =~ m/^p[pgb]m$/);
3862 return 'png' if ($ext eq "png");
705fd961 3863 return 'bmp' if ($ext eq "bmp" || $ext eq "dib");
1ec86afa 3864 return 'tga' if ($ext eq "tga");
d5477d3d 3865 return 'sgi' if ($ext eq "rgb" || $ext eq "bw" || $ext eq "sgi" || $ext eq "rgba");
02d1d628 3866 return 'gif' if ($ext eq "gif");
10461f9a 3867 return 'raw' if ($ext eq "raw");
2b405c9e 3868 return lc $ext; # best guess
02d1d628
AMH
3869 return ();
3870}
3871
9b1ec2b8
TC
3872sub combines {
3873 return @combine_types;
3874}
3875
02d1d628
AMH
3876# get the minimum of a list
3877
bf1573f9 3878sub _min {
02d1d628
AMH
3879 my $mx=shift;
3880 for(@_) { if ($_<$mx) { $mx=$_; }}
3881 return $mx;
3882}
3883
3884# get the maximum of a list
3885
bf1573f9 3886sub _max {
02d1d628
AMH
3887 my $mx=shift;
3888 for(@_) { if ($_>$mx) { $mx=$_; }}
3889 return $mx;
3890}
3891
3892# string stuff for iptc headers
3893
bf1573f9 3894sub _clean {
02d1d628
AMH
3895 my($str)=$_[0];
3896 $str = substr($str,3);
3897 $str =~ s/[\n\r]//g;
3898 $str =~ s/\s+/ /g;
3899 $str =~ s/^\s//;
3900 $str =~ s/\s$//;
3901 return $str;
3902}
3903
3904# A little hack to parse iptc headers.
3905
3906sub parseiptc {
3907 my $self=shift;
3908 my(@sar,$item,@ar);
3909 my($caption,$photogr,$headln,$credit);
3910
3911 my $str=$self->{IPTCRAW};
3912
24ae6325
TC
3913 defined $str
3914 or return;
02d1d628
AMH
3915
3916 @ar=split(/8BIM/,$str);
3917
3918 my $i=0;
3919 foreach (@ar) {
3920 if (/^\004\004/) {
3921 @sar=split(/\034\002/);
3922 foreach $item (@sar) {
cdd23610 3923 if ($item =~ m/^x/) {
bf1573f9 3924 $caption = _clean($item);
02d1d628
AMH
3925 $i++;
3926 }
cdd23610 3927 if ($item =~ m/^P/) {
bf1573f9 3928 $photogr = _clean($item);
02d1d628
AMH
3929 $i++;
3930 }
cdd23610 3931 if ($item =~ m/^i/) {
bf1573f9 3932 $headln = _clean($item);
02d1d628
AMH
3933 $i++;
3934 }
cdd23610 3935 if ($item =~ m/^n/) {
bf1573f9 3936 $credit = _clean($item);
02d1d628
AMH
3937 $i++;
3938 }
3939 }
3940 }
3941 }
3942 return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
3943}
3944
92bda632
TC
3945sub Inline {
3946 my ($lang) = @_;
3947
3948 $lang eq 'C'
3949 or die "Only C language supported";
3950
3951 require Imager::ExtUtils;
3952 return Imager::ExtUtils->inline_config;
3953}
02d1d628 3954
ffddd407
TC
3955# threads shouldn't try to close raw Imager objects
3956sub Imager::ImgRaw::CLONE_SKIP { 1 }
3957
2c331f9f
TC
3958sub preload {
3959 # this serves two purposes:
3960 # - a class method to load the file support modules included with Image
3961 # (or were included, once the library dependent modules are split out)
3962 # - something for Module::ScanDeps to analyze
3963 # https://rt.cpan.org/Ticket/Display.html?id=6566
3964 local $@;
3965 eval { require Imager::File::GIF };
3966 eval { require Imager::File::JPEG };
3967 eval { require Imager::File::PNG };
3968 eval { require Imager::File::SGI };
3969 eval { require Imager::File::TIFF };
3970 eval { require Imager::File::ICO };
3971 eval { require Imager::Font::W32 };
3972 eval { require Imager::Font::FT2 };
3973 eval { require Imager::Font::T1 };
3974}
3975
1d7e3124
TC
3976# backward compatibility for %formats
3977package Imager::FORMATS;
3978use strict;
3979use constant IX_FORMATS => 0;
3980use constant IX_LIST => 1;
3981use constant IX_INDEX => 2;
3982use constant IX_CLASSES => 3;
3983
3984sub TIEHASH {
3985 my ($class, $formats, $classes) = @_;
3986
3987 return bless [ $formats, [ ], 0, $classes ], $class;
3988}
3989
3990sub _check {
3991 my ($self, $key) = @_;
3992
3993 (my $file = $self->[IX_CLASSES]{$key} . ".pm") =~ s(::)(/)g;
3994 my $value;
3995 if (eval { require $file; 1 }) {
3996 $value = 1;
3997 }
3998 else {
3999 $value = undef;
4000 }
4001 $self->[IX_FORMATS]{$key} = $value;
4002
4003 return $value;
4004}
4005
4006sub FETCH {
4007 my ($self, $key) = @_;
4008
4009 exists $self->[IX_FORMATS]{$key} and return $self->[IX_FORMATS]{$key};
4010
4011 $self->[IX_CLASSES]{$key} or return undef;
4012
4013 return $self->_check($key);
4014}
4015
4016sub STORE {
4017 die "%Imager::formats is not user monifiable";
4018}
4019
4020sub DELETE {
4021 die "%Imager::formats is not user monifiable";
4022}
4023
4024sub CLEAR {
4025 die "%Imager::formats is not user monifiable";
4026}
4027
4028sub EXISTS {
4029 my ($self, $key) = @_;
4030
4031 if (exists $self->[IX_FORMATS]{$key}) {
4032 my $value = $self->[IX_FORMATS]{$key}
4033 or return;
4034 return 1;
4035 }
4036
4037 $self->_check($key) or return 1==0;
4038
4039 return 1==1;
4040}
4041
4042sub FIRSTKEY {
4043 my ($self) = @_;
4044
4045 unless (@{$self->[IX_LIST]}) {
4046 # full populate it
d7e4ec85
TC
4047 @{$self->[IX_LIST]} = grep $self->[IX_FORMATS]{$_},
4048 keys %{$self->[IX_FORMATS]};
1d7e3124
TC
4049
4050 for my $key (keys %{$self->[IX_CLASSES]}) {
4051 $self->[IX_FORMATS]{$key} and next;
4052 $self->_check($key)
4053 and push @{$self->[IX_LIST]}, $key;
4054 }
4055 }
4056
4057 @{$self->[IX_LIST]} or return;
4058 $self->[IX_INDEX] = 1;
4059 return $self->[IX_LIST][0];
4060}
4061
4062sub NEXTKEY {
4063 my ($self) = @_;
4064
4065 $self->[IX_INDEX] < @{$self->[IX_LIST]}
4066 or return;
4067
4068 return $self->[IX_LIST][$self->[IX_INDEX]++];
4069}
4070
4071sub SCALAR {
4072 my ($self) = @_;
4073
4074 return scalar @{$self->[IX_LIST]};
4075}
4076
02d1d628
AMH
40771;
4078__END__
4079# Below is the stub of documentation for your module. You better edit it!
4080
4081=head1 NAME
4082
4083Imager - Perl extension for Generating 24 bit Images
4084
4085=head1 SYNOPSIS
4086
0e418f1e
AMH
4087 # Thumbnail example
4088
4089 #!/usr/bin/perl -w
4090 use strict;
10461f9a 4091 use Imager;
02d1d628 4092
0e418f1e
AMH
4093 die "Usage: thumbmake.pl filename\n" if !-f $ARGV[0];
4094 my $file = shift;
4095
4096 my $format;
4097
e36d02ad 4098 # see Imager::Files for information on the read() method
c7cf66c9 4099 my $img = Imager->new(file=>$file)
2f2a6e54 4100 or die Imager->errstr();
0e418f1e
AMH
4101
4102 $file =~ s/\.[^.]*$//;
4103
4104 # Create smaller version
cf7a7d18 4105 # documented in Imager::Transformations
0e418f1e
AMH
4106 my $thumb = $img->scale(scalefactor=>.3);
4107
4108 # Autostretch individual channels
4109 $thumb->filter(type=>'autolevels');