]> git.imager.perl.org - imager.git/blobdiff - lib/Imager/Color.pm
[rt #86428] search a few more places for libraries/headers
[imager.git] / lib / Imager / Color.pm
index 2ccf089541a431b683d8f69ca504c4552a8543bd..478f437ec46e2d8f8dab947df2ab72c58bf21eab 100644 (file)
@@ -2,12 +2,14 @@ package Imager::Color;
 
 use Imager;
 use strict;
-use vars qw();
+use vars qw($VERSION);
+
+$VERSION = "1.011";
 
 # It's just a front end to the XS creation functions.
 
 # used in converting hsv to rgb
-my @hsv_map = 
+my @hsv_map =
   (
    'vkm', 'nvm', 'mvk', 'mnv', 'kmv', 'vmn'
   );
@@ -15,7 +17,7 @@ my @hsv_map =
 sub _hsv_to_rgb {
   my ($hue, $sat, $val) = @_;
 
-  # HSV conversions from pages 401-403 "Procedural Elements for Computer 
+  # HSV conversions from pages 401-403 "Procedural Elements for Computer
   # Graphics", 1985, ISBN 0-07-053534-5.
 
   my @result;
@@ -61,6 +63,8 @@ my @gimp_search =
    '/usr/share/gimp/palettes/Named_Colors',
   );
 
+my $default_gimp_palette;
+
 sub _load_gimp_palette {
   my ($filename) = @_;
 
@@ -103,12 +107,25 @@ sub _get_gimp_color {
   if ($args{palette}) {
     $filename = $args{palette};
   }
+  elsif (defined $default_gimp_palette) {
+    # don't search again and again and again ...
+    if (!length $default_gimp_palette
+       || !-f $default_gimp_palette) {
+      $Imager::ERRSTR = "No GIMP palette found";
+      $default_gimp_palette = "";
+      return;
+    }
+
+    $filename = $default_gimp_palette;
+  }
   else {
     # try to make one up - this is intended to die if tainting is
     # enabled and $ENV{HOME} is tainted.  To avoid that untaint $ENV{HOME}
     # or set the palette parameter
     for my $attempt (@gimp_search) {
       my $work = $attempt; # don't modify the source array
+      $work =~ /\$HOME/ && !defined $ENV{HOME}
+       and next;
       $work =~ s/\$HOME/$ENV{HOME}/;
       if (-e $work) {
         $filename = $work;
@@ -117,11 +134,14 @@ sub _get_gimp_color {
     }
     if (!$filename) {
       $Imager::ERRSTR = "No GIMP palette found";
+      $default_gimp_palette = "";
       return ();
     }
+
+    $default_gimp_palette = $filename;
   }
 
-  if ((!$gimp_cache{$filename} 
+  if ((!$gimp_cache{$filename}
       || (stat $filename)[9] != $gimp_cache{$filename})
      && !_load_gimp_palette($filename)) {
     return ();
@@ -135,8 +155,9 @@ sub _get_gimp_color {
   return @{$gimp_cache{$filename}{colors}{lc $args{name}}};
 }
 
-my @x_search = 
+my @x_search =
   (
+   '/usr/share/X11/rgb.txt', # newer Xorg X11 dists use this
    '/usr/lib/X11/rgb.txt', # seems fairly standard
    '/usr/local/lib/X11/rgb.txt', # seems possible
    '/usr/X11R6/lib/X11/rgb.txt', # probably the same as the first
@@ -144,6 +165,13 @@ my @x_search =
    '/usr/openwin/lib/X11/rgb.txt',
   );
 
+my $default_x_rgb;
+
+# called by the test code to check if we can test this stuff
+sub _test_x_palettes {
+  @x_search;
+}
+
 # x rgb.txt cache
 # same structure as %gimp_cache
 my %x_cache;
@@ -184,6 +212,13 @@ sub _get_x_color {
   if ($args{palette}) {
     $filename = $args{palette};
   }
+  elsif (defined $default_x_rgb) {
+    unless (length $default_x_rgb) {
+      $Imager::ERRSTR = "No X rgb.txt palette found";
+      return ();
+    }
+    $filename = $default_x_rgb;
+  }
   else {
     for my $attempt (@x_search) {
       if (-e $attempt) {
@@ -193,16 +228,19 @@ sub _get_x_color {
     }
     if (!$filename) {
       $Imager::ERRSTR = "No X rgb.txt palette found";
+      $default_x_rgb = "";
       return ();
     }
   }
 
-  if ((!$x_cache{$filename} 
-      || (stat $filename)[9] != $x_cache{$filename})
+  if ((!$x_cache{$filename}
+      || (stat $filename)[9] != $x_cache{$filename}{mod_time})
      && !_load_x_rgb($filename)) {
     return ();
   }
 
+  $default_x_rgb = $filename;
+
   if (!$x_cache{$filename}{colors}{lc $args{name}}) {
     $Imager::ERRSTR = "Color '$args{name}' isn't in $filename";
     return ();
@@ -213,10 +251,10 @@ sub _get_x_color {
 
 # Parse color spec into an a set of 4 colors
 
-sub pspec {
+sub _pspec {
   return (@_,255) if @_ == 3 && !grep /[^\d.+eE-]/, @_;
   return (@_    ) if @_ == 4 && !grep /[^\d.+eE-]/, @_;
-  if ($_[0] =~ 
+  if ($_[0] =~
       /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
     return (hex($1),hex($2),hex($3),hex($4));
   }
@@ -241,14 +279,14 @@ sub pspec {
   elsif (exists $args{grey}) {
     @result = $args{grey};
   }
-  elsif ((exists $args{red} || exists $args{r}) 
+  elsif ((exists $args{red} || exists $args{r})
          && (exists $args{green} || exists $args{g})
          && (exists $args{blue} || exists $args{b})) {
     @result = ( exists $args{red} ? $args{red} : $args{r},
                 exists $args{green} ? $args{green} : $args{g},
                 exists $args{blue} ? $args{blue} : $args{b} );
   }
-  elsif ((exists $args{hue} || exists $args{h}) 
+  elsif ((exists $args{hue} || exists $args{h})
          && (exists $args{saturation} || exists $args{'s'})
          && (exists $args{value} || exists $args{v})) {
     my $hue = exists $args{hue}        ? $args{hue}        : $args{h};
@@ -302,7 +340,7 @@ sub pspec {
   elsif (exists $args{channel0} || $args{c0}) {
     my $i = 0;
     while (exists $args{"channel$i"} || exists $args{"c$i"}) {
-      push(@result, 
+      push(@result,
            exists $args{"channel$i"} ? $args{"channel$i"} : $args{"c$i"});
       ++$i;
     }
@@ -325,13 +363,13 @@ sub pspec {
 
 sub new {
   shift; # get rid of class name.
-  my @arg = pspec(@_);
+  my @arg = _pspec(@_);
   return @arg ? new_internal($arg[0],$arg[1],$arg[2],$arg[3]) : ();
 }
 
 sub set {
   my $self = shift;
-  my @arg = pspec(@_);
+  my @arg = _pspec(@_);
   return @arg ? set_internal($self, $arg[0],$arg[1],$arg[2],$arg[3]) : ();
 }
 
@@ -349,10 +387,65 @@ sub equals {
     $left[$ch] == $right[$ch]
       or return;
   }
-  
+
   return 1;
 }
 
+sub CLONE_SKIP { 1 }
+
+# Lifted from Graphics::Color::RGB
+# Thank you very much
+sub hsv {
+    my( $self ) = @_;
+
+    my( $red, $green, $blue, $alpha ) = $self->rgba;
+    my $max = $red;
+    my $maxc = 'r';
+    my $min = $red;
+
+    if($green > $max) {
+        $max = $green;
+        $maxc = 'g';
+    }
+    if($blue > $max) {
+        $max = $blue;
+        $maxc = 'b';
+    }
+
+    if($green < $min) {
+        $min = $green;
+    }
+    if($blue < $min) {
+        $min = $blue;
+    }
+
+    my ($h, $s, $v);
+
+    if($max == $min) {
+        $h = 0;
+    }
+    elsif($maxc eq 'r') {
+        $h = 60 * (($green - $blue) / ($max - $min)) % 360;
+    }
+    elsif($maxc eq 'g') {
+        $h = (60 * (($blue - $red) / ($max - $min)) + 120);
+    }
+    elsif($maxc eq 'b') {
+        $h = (60 * (($red - $green) / ($max - $min)) + 240);
+    }
+
+    $v = $max/255;
+    if($max == 0) {
+        $s = 0;
+    }
+    else {
+        $s = 1 - ($min / $max);
+    }
+
+    return int($h), $s, $v, $alpha;
+
+}
+
 1;
 
 __END__
@@ -363,6 +456,8 @@ Imager::Color - Color handling for Imager.
 
 =head1 SYNOPSIS
 
+  use Imager;
+
   $color = Imager::Color->new($red, $green, $blue);
   $color = Imager::Color->new($red, $green, $blue, $alpha);
   $color = Imager::Color->new("#C0C0FF"); # html color specification
@@ -372,20 +467,43 @@ Imager::Color - Color handling for Imager.
   $color->set("#C0C0FF"); # html color specification
 
   ($red, $green, $blue, $alpha) = $color->rgba();
-  @hsv = $color->hsv(); # not implemented but proposed
+  @hsv = $color->hsv();
 
   $color->info();
 
-  if ($color->equals(other=>$other_color)) { 
+  if ($color->equals(other=>$other_color)) {
     ...
   }
 
 
 =head1 DESCRIPTION
 
-This module handles creating color objects used by imager.  The idea is
-that in the future this module will be able to handle colorspace calculations
-as well.
+This module handles creating color objects used by Imager.  The idea
+is that in the future this module will be able to handle color space
+calculations as well.
+
+An Imager color consists of up to four components, each in the range 0
+to 255. Unfortunately the meaning of the components can change
+depending on the type of image you're dealing with:
+
+=over
+
+=item *
+
+for 3 or 4 channel images the color components are red, green, blue,
+alpha.
+
+=item *
+
+for 1 or 2 channel images the color components are gray, alpha, with
+the other two components ignored.
+
+=back
+
+An alpha value of zero is fully transparent, an alpha value of 255 is
+fully opaque.
+
+=head1 METHODS
 
 =over 4
 
@@ -398,13 +516,14 @@ This creates a color object to pass to functions that need a color argument.
 This changes an already defined color.  Note that this does not affect any places
 where the color has been used previously.
 
-=item rgba
+=item rgba()
 
-This returns the rgba code of the color the object contains.
+This returns the red, green, blue and alpha channels of the color the
+object contains.
 
 =item info
 
-Calling info merely dumps the relevant colorcode to the log.
+Calling info merely dumps the relevant color to the log.
 
 =item equals(other=>$other_color)
 
@@ -429,20 +548,20 @@ simple numeric parameters - if you supply 3 or 4 numeric arguments, you get a co
 
 =item *
 
-a six hex digit web color, either 'RRGGBB' or '#RRGGBB'
+a six hex digit web color, either C<RRGGBB> or C<#RRGGBB>
 
 =item *
 
-an eight hex digit web color, either 'RRGGBBAA' or '#RRGGBBAA'.
+an eight hex digit web color, either C<RRGGBBAA> or C<#RRGGBBAA>.
 
 =item *
 
-a 3 hex digit web color, '#RGB' - a value of F becomes 255.
+a 3 hex digit web color, C<#RGB> - a value of F becomes 255.
 
 =item *
 
-a color name, from whichever of the gimp Named_Colors file or X
-rgb.txt is found first.  The same as using the name keyword.
+a color name, from whichever of the gimp C<Named_Colors> file or X
+C<rgb.txt> is found first.  The same as using the C<name> keyword.
 
 =back
 
@@ -462,8 +581,8 @@ The color components in the range 0 to 255.
 
 =item *
 
-'hue', 'saturation' and 'value', optionally shortened to 'h', 's' and
-'v', to specify a HSV color.  0 <= hue < 360, 0 <= s <= 1 and 0 <= v
+C<hue>, C<saturation> and C<value>, optionally shortened to C<h>, C<s> and
+C<v>, to specify a HSV color.  0 <= hue < 360, 0 <= s <= 1 and 0 <= v
 <= 1.
 
   # the same as RGB(127,255,127)
@@ -472,14 +591,14 @@ The color components in the range 0 to 255.
 
 =item *
 
-'web', which can specify a 6 or 3 hex digit web color, in any of the
-forms '#RRGGBB', '#RGB', 'RRGGBB' or 'RGB'.
+C<web>, which can specify a 6 or 3 hex digit web color, in any of the
+forms C<#RRGGBB>, C<#RGB>, C<RRGGBB> or C<RGB>.
 
   my $c1 = Imager::Color->new(web=>'#FFC0C0'); # pale red
 
 =item *
 
-'gray' or 'grey' which specifies a single channel, from 0 to 255.
+C<gray> or C<grey> which specifies a single channel, from 0 to 255.
 
   # exactly the same
   my $c1 = Imager::Color->new(gray=>128);
@@ -487,7 +606,7 @@ forms '#RRGGBB', '#RGB', 'RRGGBB' or 'RGB'.
 
 =item *
 
-'rgb' which takes a 3 member arrayref, containing each of the red,
+C<rgb> which takes a 3 member arrayref, containing each of the red,
 green and blue values.
 
   # the same
@@ -496,7 +615,7 @@ green and blue values.
 
 =item *
 
-'hsv' which takes a 3 member arrayref, containting each of hue,
+C<hsv> which takes a 3 member arrayref, containing each of hue,
 saturation and value.
 
   # the same
@@ -505,43 +624,43 @@ saturation and value.
 
 =item *
 
-'gimp' which specifies a color from a GIMP palette file.  You can
-specify the filename of the palette file with the 'palette' parameter,
-or let Imager::Color look in various places, typically
-"$HOME/gimp-1.x/palettes/Named_Colors" with and without the version
-number, and in /usr/share/gimp/palettes/.  The palette file must have
-color names.
+C<gimp> which specifies a color from a GIMP palette file.  You can
+specify the file name of the palette file with the 'palette'
+parameter, or let Imager::Color look in various places, typically
+C<$HOME/gimp-1.x/palettes/Named_Colors> with and without the version
+number, and in C</usr/share/gimp/palettes/>.  The palette file must
+have color names.
 
   my $c1 = Imager::Color->new(gimp=>'snow');
   my $c1 = Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal);
 
 =item *
 
-'xname' which specifies a color from an X11 rgb.txt file.  You can
-specify the filename of the rgb.txt file with the 'palette' parameter,
-or let Imager::Color look in various places, typically
-'/usr/lib/X11/rgb.txt'.
+C<xname> which specifies a color from an X11 C<rgb.txt> file.  You can
+specify the file name of the C<rgb.txt> file with the C<palette>
+parameter, or let Imager::Color look in various places, typically
+C</usr/lib/X11/rgb.txt>.
 
   my $c1 = Imager::Color->new(xname=>'blue') # usually RGB(0, 0, 255)
 
 =item *
 
-'builtin' which specifies a color from the built-in color table in
+C<builtin> which specifies a color from the built-in color table in
 Imager::Color::Table.  The colors in this module are the same as the
-default X11 rgb.txt file.
+default X11 C<rgb.txt> file.
 
   my $c1 = Imager::Color->new(builtin=>'black') # always RGB(0, 0, 0)
 
 =item *
 
-'name' which specifies a name from either a GIMP palette, an X rgb.txt
-file or the built-in color table, whichever is found first.
+C<name> which specifies a name from either a GIMP palette, an X
+C<rgb.txt> file or the built-in color table, whichever is found first.
 
 =item *
 
 'channel0', 'channel1', etc, each of which specifies a single channel.  These can be abbreviated to 'c0', 'c1' etc.
 
-=item * 
+=item *
 
 'channels' which takes an arrayref of the channel values.
 
@@ -554,15 +673,27 @@ These color specifications can be used for both constructing new
 colors with the new() method and modifying existing colors with the
 set() method.
 
+=head1 METHODS
+
+=over
+
+=item hsv()
+
+    my($h, $s, $v, $alpha) = $color->hsv();
+
+Returns the color as a Hue/Saturation/Value/Alpha tuple.
+
+=back
+
 =head1 AUTHOR
 
 Arnar M. Hrafnkelsson, addi@umich.edu
-And a great deal of help from others - see the README for a complete
+And a great deal of help from others - see the C<README> for a complete
 list.
 
 =head1 SEE ALSO
 
-Imager(3)
-http://www.eecs.umich.edu/~addi/perl/Imager/
+Imager(3), Imager::Color
+http://imager.perl.org/
 
 =cut