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