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