add red, green, blue, alpha methods to color objects
[imager.git] / lib / Imager / Color.pm
CommitLineData
02d1d628
AMH
1package Imager::Color;
2
3use Imager;
4use strict;
f17b46d8
TC
5use vars qw($VERSION);
6
0231b97e 7$VERSION = "1.013";
02d1d628
AMH
8
9# It's just a front end to the XS creation functions.
10
faa9b3e7 11# used in converting hsv to rgb
bb138da3 12my @hsv_map =
faa9b3e7
TC
13 (
14 'vkm', 'nvm', 'mvk', 'mnv', 'kmv', 'vmn'
15 );
16
17sub _hsv_to_rgb {
18 my ($hue, $sat, $val) = @_;
19
bb138da3 20 # HSV conversions from pages 401-403 "Procedural Elements for Computer
faa9b3e7
TC
21 # Graphics", 1985, ISBN 0-07-053534-5.
22
23 my @result;
24 if ($sat <= 0) {
25 return ( 255 * $val, 255 * $val, 255 * $val );
26 }
27 else {
28 $val >= 0 or $val = 0;
29 $val <= 1 or $val = 1;
30 $sat <= 1 or $sat = 1;
31 $hue >= 360 and $hue %= 360;
32 $hue < 0 and $hue += 360;
33 $hue /= 60.0;
34 my $i = int($hue);
35 my $f = $hue - $i;
36 $val *= 255;
37 my $m = $val * (1.0 - $sat);
38 my $n = $val * (1.0 - $sat * $f);
39 my $k = $val * (1.0 - $sat * (1 - $f));
40 my $v = $val;
9d540150 41 my %fields = ( 'm'=>$m, 'n'=>$n, 'v'=>$v, 'k'=>$k, );
faa9b3e7
TC
42 return @fields{split //, $hsv_map[$i]};
43 }
44}
45
46# cache of loaded gimp files
08af8ecb 47# each key is a filename, under each key is a hashref with the following
faa9b3e7
TC
48# keys:
49# mod_time => last mod_time of file
50# colors => hashref name to arrayref of colors
51my %gimp_cache;
52
53# palette search locations
54# this is pretty rude
55# $HOME is replaced at runtime
56my @gimp_search =
57 (
58 '$HOME/.gimp-1.2/palettes/Named_Colors',
59 '$HOME/.gimp-1.1/palettes/Named_Colors',
60 '$HOME/.gimp/palettes/Named_Colors',
61 '/usr/share/gimp/1.2/palettes/Named_Colors',
62 '/usr/share/gimp/1.1/palettes/Named_Colors',
63 '/usr/share/gimp/palettes/Named_Colors',
64 );
65
173c91ba
TC
66my $default_gimp_palette;
67
faa9b3e7
TC
68sub _load_gimp_palette {
69 my ($filename) = @_;
70
71 if (open PAL, "< $filename") {
72 my $hdr = <PAL>;
73 chomp $hdr;
74 unless ($hdr =~ /GIMP Palette/) {
75 close PAL;
76 $Imager::ERRSTR = "$filename is not a GIMP palette file";
77 return;
78 }
79 my $line;
80 my %pal;
81 my $mod_time = (stat PAL)[9];
82 while (defined($line = <PAL>)) {
83 next if $line =~ /^#/ || $line =~ /^\s*$/;
84 chomp $line;
85 my ($r,$g, $b, $name) = split ' ', $line, 4;
86 if ($name) {
87 $name =~ s/\s*\([\d\s]+\)\s*$//;
88 $pal{lc $name} = [ $r, $g, $b ];
89 }
90 }
91 close PAL;
92
93 $gimp_cache{$filename} = { mod_time=>$mod_time, colors=>\%pal };
94
95 return 1;
96 }
97 else {
98 $Imager::ERRSTR = "Cannot open palette file $filename: $!";
99 return;
100 }
101}
102
103sub _get_gimp_color {
104 my %args = @_;
105
106 my $filename;
107 if ($args{palette}) {
108 $filename = $args{palette};
109 }
173c91ba
TC
110 elsif (defined $default_gimp_palette) {
111 # don't search again and again and again ...
112 if (!length $default_gimp_palette
113 || !-f $default_gimp_palette) {
114 $Imager::ERRSTR = "No GIMP palette found";
115 $default_gimp_palette = "";
116 return;
117 }
118
119 $filename = $default_gimp_palette;
120 }
faa9b3e7
TC
121 else {
122 # try to make one up - this is intended to die if tainting is
123 # enabled and $ENV{HOME} is tainted. To avoid that untaint $ENV{HOME}
124 # or set the palette parameter
125 for my $attempt (@gimp_search) {
126 my $work = $attempt; # don't modify the source array
3d782fde
TC
127 $work =~ /\$HOME/ && !defined $ENV{HOME}
128 and next;
faa9b3e7
TC
129 $work =~ s/\$HOME/$ENV{HOME}/;
130 if (-e $work) {
131 $filename = $work;
132 last;
133 }
134 }
135 if (!$filename) {
136 $Imager::ERRSTR = "No GIMP palette found";
173c91ba 137 $default_gimp_palette = "";
faa9b3e7
TC
138 return ();
139 }
173c91ba
TC
140
141 $default_gimp_palette = $filename;
faa9b3e7
TC
142 }
143
bb138da3 144 if ((!$gimp_cache{$filename}
faa9b3e7
TC
145 || (stat $filename)[9] != $gimp_cache{$filename})
146 && !_load_gimp_palette($filename)) {
147 return ();
148 }
149
150 if (!$gimp_cache{$filename}{colors}{lc $args{name}}) {
151 $Imager::ERRSTR = "Color '$args{name}' isn't in $filename";
152 return ();
153 }
154
155 return @{$gimp_cache{$filename}{colors}{lc $args{name}}};
156}
157
bb138da3 158my @x_search =
faa9b3e7 159 (
d034a178 160 '/usr/share/X11/rgb.txt', # newer Xorg X11 dists use this
faa9b3e7
TC
161 '/usr/lib/X11/rgb.txt', # seems fairly standard
162 '/usr/local/lib/X11/rgb.txt', # seems possible
163 '/usr/X11R6/lib/X11/rgb.txt', # probably the same as the first
08af8ecb
AMH
164 '/usr/openwin/lib/rgb.txt',
165 '/usr/openwin/lib/X11/rgb.txt',
faa9b3e7
TC
166 );
167
173c91ba
TC
168my $default_x_rgb;
169
d034a178
TC
170# called by the test code to check if we can test this stuff
171sub _test_x_palettes {
172 @x_search;
173}
174
faa9b3e7
TC
175# x rgb.txt cache
176# same structure as %gimp_cache
177my %x_cache;
178
179sub _load_x_rgb {
180 my ($filename) = @_;
181
182 local *RGB;
183 if (open RGB, "< $filename") {
184 my $line;
185 my %pal;
186 my $mod_time = (stat RGB)[9];
187 while (defined($line = <RGB>)) {
188 # the version of rgb.txt supplied with GNU Emacs uses # for comments
189 next if $line =~ /^[!#]/ || $line =~ /^\s*$/;
190 chomp $line;
191 my ($r,$g, $b, $name) = split ' ', $line, 4;
192 if ($name) {
193 $pal{lc $name} = [ $r, $g, $b ];
194 }
195 }
196 close RGB;
197
198 $x_cache{$filename} = { mod_time=>$mod_time, colors=>\%pal };
199
200 return 1;
201 }
202 else {
203 $Imager::ERRSTR = "Cannot open palette file $filename: $!";
204 return;
205 }
206}
207
208sub _get_x_color {
209 my %args = @_;
210
211 my $filename;
212 if ($args{palette}) {
213 $filename = $args{palette};
214 }
173c91ba
TC
215 elsif (defined $default_x_rgb) {
216 unless (length $default_x_rgb) {
217 $Imager::ERRSTR = "No X rgb.txt palette found";
218 return ();
219 }
220 $filename = $default_x_rgb;
221 }
faa9b3e7
TC
222 else {
223 for my $attempt (@x_search) {
224 if (-e $attempt) {
225 $filename = $attempt;
226 last;
227 }
228 }
229 if (!$filename) {
230 $Imager::ERRSTR = "No X rgb.txt palette found";
173c91ba 231 $default_x_rgb = "";
faa9b3e7
TC
232 return ();
233 }
234 }
235
bb138da3 236 if ((!$x_cache{$filename}
173c91ba 237 || (stat $filename)[9] != $x_cache{$filename}{mod_time})
faa9b3e7
TC
238 && !_load_x_rgb($filename)) {
239 return ();
240 }
241
173c91ba
TC
242 $default_x_rgb = $filename;
243
faa9b3e7
TC
244 if (!$x_cache{$filename}{colors}{lc $args{name}}) {
245 $Imager::ERRSTR = "Color '$args{name}' isn't in $filename";
246 return ();
247 }
248
249 return @{$x_cache{$filename}{colors}{lc $args{name}}};
250}
02d1d628
AMH
251
252# Parse color spec into an a set of 4 colors
253
d5556805 254sub _pspec {
faa9b3e7
TC
255 return (@_,255) if @_ == 3 && !grep /[^\d.+eE-]/, @_;
256 return (@_ ) if @_ == 4 && !grep /[^\d.+eE-]/, @_;
bb138da3 257 if ($_[0] =~
02d1d628
AMH
258 /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
259 return (hex($1),hex($2),hex($3),hex($4));
260 }
261 if ($_[0] =~ /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
262 return (hex($1),hex($2),hex($3),255);
263 }
faa9b3e7
TC
264 if ($_[0] =~ /^\#([\da-f])([\da-f])([\da-f])$/i) {
265 return (hex($1) * 17, hex($2) * 17, hex($3) * 17, 255);
266 }
267 my %args;
268 if (@_ == 1) {
269 # a named color
270 %args = ( name => @_ );
271 }
272 else {
273 %args = @_;
274 }
275 my @result;
276 if (exists $args{gray}) {
277 @result = $args{gray};
278 }
279 elsif (exists $args{grey}) {
280 @result = $args{grey};
281 }
bb138da3 282 elsif ((exists $args{red} || exists $args{r})
faa9b3e7
TC
283 && (exists $args{green} || exists $args{g})
284 && (exists $args{blue} || exists $args{b})) {
285 @result = ( exists $args{red} ? $args{red} : $args{r},
286 exists $args{green} ? $args{green} : $args{g},
287 exists $args{blue} ? $args{blue} : $args{b} );
288 }
bb138da3 289 elsif ((exists $args{hue} || exists $args{h})
faa9b3e7
TC
290 && (exists $args{saturation} || exists $args{'s'})
291 && (exists $args{value} || exists $args{v})) {
292 my $hue = exists $args{hue} ? $args{hue} : $args{h};
293 my $sat = exists $args{saturation} ? $args{saturation} : $args{'s'};
294 my $val = exists $args{value} ? $args{value} : $args{v};
08af8ecb 295
faa9b3e7
TC
296 @result = _hsv_to_rgb($hue, $sat, $val);
297 }
298 elsif (exists $args{web}) {
299 if ($args{web} =~ /^#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])$/i) {
300 @result = (hex($1),hex($2),hex($3));
301 }
302 elsif ($args{web} =~ /^#?([\da-f])([\da-f])([\da-f])$/i) {
303 @result = (hex($1) * 17, hex($2) * 17, hex($3) * 17);
304 }
305 }
306 elsif ($args{name}) {
307 unless (@result = _get_gimp_color(%args)) {
308 unless (@result = _get_x_color(%args)) {
1c00d65b
TC
309 require Imager::Color::Table;
310 unless (@result = Imager::Color::Table->get($args{name})) {
311 $Imager::ERRSTR = "No color named $args{name} found";
312 return ();
313 }
faa9b3e7
TC
314 }
315 }
316 }
317 elsif ($args{gimp}) {
318 @result = _get_gimp_color(name=>$args{gimp}, %args);
319 }
320 elsif ($args{xname}) {
321 @result = _get_x_color(name=>$args{xname}, %args);
322 }
1c00d65b
TC
323 elsif ($args{builtin}) {
324 require Imager::Color::Table;
325 @result = Imager::Color::Table->get($args{builtin});
326 }
faa9b3e7
TC
327 elsif ($args{rgb}) {
328 @result = @{$args{rgb}};
329 }
330 elsif ($args{rgba}) {
331 @result = @{$args{rgba}};
332 return @result if @result == 4;
333 }
334 elsif ($args{hsv}) {
335 @result = _hsv_to_rgb(@{$args{hsv}});
336 }
337 elsif ($args{channels}) {
d1baf8e4
TC
338 my @ch = @{$args{channels}};
339 return ( @ch, (0) x (4 - @ch) );
faa9b3e7
TC
340 }
341 elsif (exists $args{channel0} || $args{c0}) {
342 my $i = 0;
343 while (exists $args{"channel$i"} || exists $args{"c$i"}) {
bb138da3 344 push(@result,
faa9b3e7
TC
345 exists $args{"channel$i"} ? $args{"channel$i"} : $args{"c$i"});
346 ++$i;
347 }
348 }
349 else {
350 $Imager::ERRSTR = "No color specification found";
351 return ();
352 }
353 if (@result) {
354 if (exists $args{alpha} || exists $args{a}) {
355 push(@result, exists $args{alpha} ? $args{alpha} : $args{a});
356 }
357 while (@result < 4) {
358 push(@result, 255);
359 }
360 return @result;
361 }
02d1d628
AMH
362 return ();
363}
364
02d1d628
AMH
365sub new {
366 shift; # get rid of class name.
d5556805 367 my @arg = _pspec(@_);
02d1d628
AMH
368 return @arg ? new_internal($arg[0],$arg[1],$arg[2],$arg[3]) : ();
369}
370
371sub set {
372 my $self = shift;
d5556805 373 my @arg = _pspec(@_);
02d1d628
AMH
374 return @arg ? set_internal($self, $arg[0],$arg[1],$arg[2],$arg[3]) : ();
375}
376
5f8cbeac
TC
377sub equals {
378 my ($self, %opts) = @_;
379
380 my $other = $opts{other}
381 or return Imager->_set_error("'other' parameter required");
382 my $ignore_alpha = $opts{ignore_alpha} || 0;
383
384 my @left = $self->rgba;
385 my @right = $other->rgba;
386 my $last_chan = $ignore_alpha ? 2 : 3;
387 for my $ch (0 .. $last_chan) {
388 $left[$ch] == $right[$ch]
389 or return;
390 }
bb138da3 391
5f8cbeac
TC
392 return 1;
393}
394
ffddd407
TC
395sub CLONE_SKIP { 1 }
396
bb138da3 397# Lifted from Graphics::Color::RGB
d5fb1fdf
PG
398# Thank you very much
399sub hsv {
400 my( $self ) = @_;
401
402 my( $red, $green, $blue, $alpha ) = $self->rgba;
403 my $max = $red;
404 my $maxc = 'r';
405 my $min = $red;
406
407 if($green > $max) {
bb138da3
TC
408 $max = $green;
409 $maxc = 'g';
d5fb1fdf
PG
410 }
411 if($blue > $max) {
bb138da3
TC
412 $max = $blue;
413 $maxc = 'b';
d5fb1fdf
PG
414 }
415
416 if($green < $min) {
bb138da3 417 $min = $green;
d5fb1fdf
PG
418 }
419 if($blue < $min) {
bb138da3 420 $min = $blue;
d5fb1fdf
PG
421 }
422
423 my ($h, $s, $v);
424
425 if($max == $min) {
426 $h = 0;
bb138da3 427 }
d5fb1fdf
PG
428 elsif($maxc eq 'r') {
429 $h = 60 * (($green - $blue) / ($max - $min)) % 360;
bb138da3 430 }
d5fb1fdf
PG
431 elsif($maxc eq 'g') {
432 $h = (60 * (($blue - $red) / ($max - $min)) + 120);
bb138da3 433 }
d5fb1fdf
PG
434 elsif($maxc eq 'b') {
435 $h = (60 * (($red - $green) / ($max - $min)) + 240);
436 }
437
438 $v = $max/255;
439 if($max == 0) {
440 $s = 0;
bb138da3 441 }
d5fb1fdf
PG
442 else {
443 $s = 1 - ($min / $max);
444 }
445
446 return int($h), $s, $v, $alpha;
447
448}
449
02d1d628
AMH
4501;
451
452__END__
453
454=head1 NAME
455
456Imager::Color - Color handling for Imager.
457
458=head1 SYNOPSIS
459
668c4f62
TC
460 use Imager;
461
02d1d628
AMH
462 $color = Imager::Color->new($red, $green, $blue);
463 $color = Imager::Color->new($red, $green, $blue, $alpha);
464 $color = Imager::Color->new("#C0C0FF"); # html color specification
465
466 $color->set($red, $green, $blue);
467 $color->set($red, $green, $blue, $alpha);
468 $color->set("#C0C0FF"); # html color specification
469
470 ($red, $green, $blue, $alpha) = $color->rgba();
d5fb1fdf 471 @hsv = $color->hsv();
02d1d628
AMH
472
473 $color->info();
474
bb138da3 475 if ($color->equals(other=>$other_color)) {
5f8cbeac
TC
476 ...
477 }
478
02d1d628
AMH
479
480=head1 DESCRIPTION
481
5715f7c3
TC
482This module handles creating color objects used by Imager. The idea
483is that in the future this module will be able to handle color space
484calculations as well.
02d1d628 485
0462442b
TC
486An Imager color consists of up to four components, each in the range 0
487to 255. Unfortunately the meaning of the components can change
488depending on the type of image you're dealing with:
489
490=over
491
492=item *
493
494for 3 or 4 channel images the color components are red, green, blue,
495alpha.
496
497=item *
498
499for 1 or 2 channel images the color components are gray, alpha, with
500the other two components ignored.
501
502=back
503
504An alpha value of zero is fully transparent, an alpha value of 255 is
505fully opaque.
506
42f8a929 507=head1 METHODS
0462442b 508
02d1d628
AMH
509=over 4
510
511=item new
512
513This creates a color object to pass to functions that need a color argument.
514
515=item set
516
517This changes an already defined color. Note that this does not affect any places
518where the color has been used previously.
519
5715f7c3 520=item rgba()
02d1d628 521
5715f7c3
TC
522This returns the red, green, blue and alpha channels of the color the
523object contains.
02d1d628
AMH
524
525=item info
526
5715f7c3 527Calling info merely dumps the relevant color to the log.
02d1d628 528
5f8cbeac
TC
529=item equals(other=>$other_color)
530
531=item equals(other=>$other_color, ignore_alpha=>1)
532
533Compares $self and color $other_color returning true if the color
534components are the same.
535
536Compares all four channels unless C<ignore_alpha> is set. If
537C<ignore_alpha> is set only the first three channels are compared.
538
02d1d628
AMH
539=back
540
faa9b3e7
TC
541You can specify colors in several different ways, you can just supply
542simple values:
543
544=over
545
546=item *
547
548simple numeric parameters - if you supply 3 or 4 numeric arguments, you get a color made up of those RGB (and possibly A) components.
549
550=item *
551
5715f7c3 552a six hex digit web color, either C<RRGGBB> or C<#RRGGBB>
faa9b3e7
TC
553
554=item *
555
5715f7c3 556an eight hex digit web color, either C<RRGGBBAA> or C<#RRGGBBAA>.
faa9b3e7
TC
557
558=item *
559
5715f7c3 560a 3 hex digit web color, C<#RGB> - a value of F becomes 255.
faa9b3e7
TC
561
562=item *
563
5715f7c3
TC
564a color name, from whichever of the gimp C<Named_Colors> file or X
565C<rgb.txt> is found first. The same as using the C<name> keyword.
faa9b3e7
TC
566
567=back
568
569You can supply named parameters:
570
571=over
572
573=item *
574
575'red', 'green' and 'blue', optionally shortened to 'r', 'g' and 'b'.
576The color components in the range 0 to 255.
577
578 # all of the following are equivalent
579 my $c1 = Imager::Color->new(red=>100, blue=>255, green=>0);
580 my $c2 = Imager::Color->new(r=>100, b=>255, g=>0);
581 my $c3 = Imager::Color->new(r=>100, blue=>255, g=>0);
582
583=item *
584
5715f7c3
TC
585C<hue>, C<saturation> and C<value>, optionally shortened to C<h>, C<s> and
586C<v>, to specify a HSV color. 0 <= hue < 360, 0 <= s <= 1 and 0 <= v
faa9b3e7
TC
587<= 1.
588
589 # the same as RGB(127,255,127)
590 my $c1 = Imager::Color->new(hue=>120, v=>1, s=>0.5);
591 my $c1 = Imager::Color->new(hue=>120, value=>1, saturation=>0.5);
592
593=item *
594
5715f7c3
TC
595C<web>, which can specify a 6 or 3 hex digit web color, in any of the
596forms C<#RRGGBB>, C<#RGB>, C<RRGGBB> or C<RGB>.
faa9b3e7
TC
597
598 my $c1 = Imager::Color->new(web=>'#FFC0C0'); # pale red
599
600=item *
601
5715f7c3 602C<gray> or C<grey> which specifies a single channel, from 0 to 255.
faa9b3e7
TC
603
604 # exactly the same
605 my $c1 = Imager::Color->new(gray=>128);
606 my $c1 = Imager::Color->new(grey=>128);
607
608=item *
609
5715f7c3 610C<rgb> which takes a 3 member arrayref, containing each of the red,
faa9b3e7
TC
611green and blue values.
612
613 # the same
614 my $c1 = Imager::Color->new(rgb=>[255, 100, 0]);
615 my $c1 = Imager::Color->new(r=>255, g=>100, b=>0);
616
617=item *
618
5715f7c3 619C<hsv> which takes a 3 member arrayref, containing each of hue,
faa9b3e7
TC
620saturation and value.
621
622 # the same
623 my $c1 = Imager::Color->new(hsv=>[120, 0.5, 1]);
624 my $c1 = Imager::Color->new(hue=>120, v=>1, s=>0.5);
625
626=item *
627
5715f7c3
TC
628C<gimp> which specifies a color from a GIMP palette file. You can
629specify the file name of the palette file with the 'palette'
630parameter, or let Imager::Color look in various places, typically
631C<$HOME/gimp-1.x/palettes/Named_Colors> with and without the version
632number, and in C</usr/share/gimp/palettes/>. The palette file must
633have color names.
faa9b3e7
TC
634
635 my $c1 = Imager::Color->new(gimp=>'snow');
636 my $c1 = Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal);
637
638=item *
639
5715f7c3
TC
640C<xname> which specifies a color from an X11 C<rgb.txt> file. You can
641specify the file name of the C<rgb.txt> file with the C<palette>
642parameter, or let Imager::Color look in various places, typically
643C</usr/lib/X11/rgb.txt>.
faa9b3e7
TC
644
645 my $c1 = Imager::Color->new(xname=>'blue') # usually RGB(0, 0, 255)
646
647=item *
648
5715f7c3 649C<builtin> which specifies a color from the built-in color table in
1c00d65b 650Imager::Color::Table. The colors in this module are the same as the
5715f7c3 651default X11 C<rgb.txt> file.
1c00d65b
TC
652
653 my $c1 = Imager::Color->new(builtin=>'black') # always RGB(0, 0, 0)
654
655=item *
656
5715f7c3
TC
657C<name> which specifies a name from either a GIMP palette, an X
658C<rgb.txt> file or the built-in color table, whichever is found first.
faa9b3e7
TC
659
660=item *
661
662'channel0', 'channel1', etc, each of which specifies a single channel. These can be abbreviated to 'c0', 'c1' etc.
663
bb138da3 664=item *
faa9b3e7
TC
665
666'channels' which takes an arrayref of the channel values.
667
668=back
669
670Optionally you can add an alpha channel to a color with the 'alpha' or
671'a' parameter.
672
673These color specifications can be used for both constructing new
674colors with the new() method and modifying existing colors with the
675set() method.
676
d5fb1fdf
PG
677=head1 METHODS
678
d2549cc4
TC
679=over
680
681=item hsv()
d5fb1fdf 682
d2549cc4 683 my($h, $s, $v, $alpha) = $color->hsv();
d5fb1fdf 684
d2549cc4
TC
685Returns the color as a Hue/Saturation/Value/Alpha tuple.
686
0231b97e
TC
687=item red
688
689=item green
690
691=item blue
692
693=item alpha
694
695Returns the respective component as an integer from 0 to 255.
696
d2549cc4 697=back
d5fb1fdf 698
02d1d628
AMH
699=head1 AUTHOR
700
701Arnar M. Hrafnkelsson, addi@umich.edu
5715f7c3 702And a great deal of help from others - see the C<README> for a complete
02d1d628
AMH
703list.
704
705=head1 SEE ALSO
706
0462442b 707Imager(3), Imager::Color
8f22b8d8 708http://imager.perl.org/
02d1d628
AMH
709
710=cut