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