package col_space;
use strict;
use warnings;
use Exporter;
our @ISA=qw/Exporter/;
our @EXPORT=qw/rgb_to_hsl rgb_to_hsv rgb_to_cmy rgb_to_xyz
hsl_to_rgb hsv_to_rgb cmy_to_rgb xyz_to_rgb/;
sub min3($$$){
my $res=$_[0];
$res=$_[1] if $res > $_[1];
$res=$_[2] if $res > $_[2];
$res;
}
sub max3($$$){
my $res=$_[0];
$res=$_[1] if $res < $_[1];
$res=$_[2] if $res < $_[2];
$res;
}
sub hue_2_rgb($$$){
my ($v1,$v2,$vh)=@_;
$vh+=1.0 if $vh < 0.0;
$vh-=1.0 if $vh > 1.0;
return $v1 + ($v2 - $v1) * 6.0 * $vh if 6.0 * $vh < 1;
return $v2 if 2.0 * $vh < 1;
return $v1 + ($v2 - $v1) * ((2.0/3.0) - $vh)*6.0 if 3.0 * $vh < 2;
return $v1;
}
sub rgb_to_hsl {
return unless defined wantarray;
my ($r,$g,$b);
my ($h,$s,$l)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($r,$g,$b)=($_[0]->{R},$_[0]->{G},$_[0]->{B});
}else{
($r,$g,$b)=@_;
}
if($r<0 || $r>1
|| $g<0 || $g>1
|| $b<0 || $b>1){
die "bad input for rgb_to_hsl: $r $g $b";
}
my $var_Min = min3($r,$g,$b);
my $var_Max = max3($r,$g,$b);
my $del_Max = $var_Max - $var_Min;
$l = ( $var_Max + $var_Min ) / 2.0;
if( $del_Max == 0.0 ){
$h=$s=0.0;
}else{
$s=$del_Max / (2.0 - $var_Max - $var_Min);
$s=$del_Max / ($var_Max + $var_Min) if $l < 0.5;
my $del_R=( ( ( $var_Max - $r ) / 6.0 ) + ( $del_Max / 2.0 ) ) / $
+del_Max;
my $del_G=( ( ( $var_Max - $g ) / 6.0 ) + ( $del_Max / 2.0 ) ) / $
+del_Max;
my $del_B=( ( ( $var_Max - $b ) / 6.0 ) + ( $del_Max / 2.0 ) ) / $
+del_Max;
if ( $r == $var_Max ) {$h = $del_B - $del_G }
elsif ( $g == $var_Max ) {$h = ( 1.0 / 3.0 ) + $del_R - $del_B}
elsif ( $b == $var_Max ) {$h = ( 2.0 / 3.0 ) + $del_G - $del_R};
$h+=1.0 if $h < 0;
$h-=1.0 if $h > 1.0;
}
return ($h,$s,$l) if wantarray;
return {H=>$h, S=>$s, L=>$l};
}
sub hsl_to_rgb {
return unless defined wantarray;
my ($h,$s,$l);
my ($r,$g,$b)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($h,$s,$l)=($_[0]->{H},$_[0]->{S},$_[0]->{L});
}else{
($h,$s,$l)=@_;
}
if($h<0 || $s>1
|| $s<0 || $h>1
|| $l<0 || $l>1){
die "bad input for hsl_to_rgb $h $s $l";
}
my ($var_1,$var_2);
if (!$s){
$r=$g=$b=$l;
}else{
if( $l < 0.5 ){ $var_2 = $l * ( 1 + $s ); }
else { $var_2 = ( $l + $s ) - ( $s * $l ) };
$var_1 = 2.0 * $l - $var_2;
$r = hue_2_rgb( $var_1, $var_2, $h + ( 1.0 / 3.0 ) ) ;
$g = hue_2_rgb( $var_1, $var_2, $h );
$b = hue_2_rgb( $var_1, $var_2, $h - ( 1.0/ 3.0 ) );
}
return ($r,$g,$b) if wantarray;
return {R=>$r, G=>$g, B=>$b};
}
sub rgb_to_hsv {
return unless defined wantarray;
my ($r,$g,$b);
my ($h,$s,$v)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($r,$g,$b)=($_[0]->{R},$_[0]->{G},$_[0]->{B});
}else{
($r,$g,$b)=@_;
}
if($r<0 || $r>1
|| $g<0 || $g>1
|| $b<0 || $b>1){
die "bad input for rgb_to_hsv: $r $g $b";
}
my $var_Min = min3($r,$g,$b);
my $var_Max = max3($r,$g,$b);
my $del_Max = $var_Max - $var_Min;
$v=$var_Max;
if ( !$del_Max){
$h=$s=0.0;
}else{
$s = $del_Max / $var_Max;
my $del_R = ( ( ( $var_Max - $r ) / 6.0 ) + ( $del_Max / 2.0 ) ) /
+ $del_Max;
my $del_G = ( ( ( $var_Max - $g ) / 6.0 ) + ( $del_Max / 2.0 ) ) /
+ $del_Max;
my $del_B = ( ( ( $var_Max - $b ) / 6.0 ) + ( $del_Max / 2.0 ) ) /
+ $del_Max;
if ( $r == $var_Max ) { $h = $del_B - $del_G; }
elsif ( $g == $var_Max ) { $h = ( 1.0 / 3.0 ) + $del_R - $del_B; }
elsif ( $b == $var_Max ) { $h = ( 2.0 / 3.0 ) + $del_G - $del_R; }
else { die 'internal failure'; }
$h+=1.0 if $h < 0.0;
$h-=1.0 if $h > 1.0;
}
return ($h,$s,$v) if wantarray;
return {H=>$h, S=>$s, V=>$v};
}
sub hsv_to_rgb {
return unless defined wantarray;
my ($h,$s,$v);
my ($r,$g,$b)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($h,$s,$v)=($_[0]->{H},$_[0]->{S},$_[0]->{V});
}else{
($h,$s,$v)=@_;
}
if($h<0 || $h>1
|| $s<0 || $s>1
|| $v<0 || $v>1){
die "bad input for hsv_to_rgb: $h $s $v";
}
if ( !$s ){
$r=$g=$b=$v;
}else{
my $var_h = $h * 6.0;
my $var_i =int $var_h;
my $var_1 = $v * ( 1 - $s );
my $var_2 = $v * ( 1 - $s * ( $var_h - $var_i ) );
my $var_3 = $v * ( 1 - $s * ( 1 - ( $var_h - $var_i ) ) );
if ( $var_i == 0 ) { $r = $v ; $g = $var_3 ; $b = $var_1;
+ }
elsif ( $var_i == 1 ) { $r = $var_2 ; $g = $v ; $b = $var_1;
+ }
elsif ( $var_i == 2 ) { $r = $var_1 ; $g = $v ; $b = $var_3;
+ }
elsif ( $var_i == 3 ) { $r = $var_1 ; $g = $var_2 ; $b = $v ;
+ }
elsif ( $var_i == 4 ) { $r = $var_3 ; $g = $var_1 ; $b = $v ;
+ }
elsif ( $var_i == 5 ) { $r = $v ; $g = $var_1 ; $b = $var_2;
+ }
else { die 'internal failure'; }
}
return ($r,$g,$b) if wantarray;
return {R=>$r, G=>$g, B=>$b};
}
sub rgb_to_cmy {
return unless defined wantarray;
my ($c,$m,$y);
my ($r,$g,$b)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($r,$g,$b)=($_[0]->{R},$_[0]->{G},$_[0]->{B});
}else{
($r,$g,$b)=@_;
}
if($r<0 || $r>1
|| $g<0 || $g>1
|| $b<0 || $b>1){
die "bad input for rgb_to_cmy $r $g $b";
}
($c,$m,$y)=(1-$r,1-$g,1-$b);
return ($c,$m,$y) if wantarray;
return {C=>$r, M=>$g, Y=>$b};
}
sub cmy_to_rgb {
return unless defined wantarray;
my ($c,$m,$y);
my ($r,$g,$b)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($c,$m,$y)=($_[0]->{C},$_[0]->{M},$_[0]->{Y});
}else{
($c,$m,$y)=@_;
}
if($c<0 || $c>1
|| $m<0 || $m>1
|| $y<0 || $y>1){
die "bad input for cmy_to_rgb $c $m $y";
}
($r,$g,$b)=(1-$c,1-$m,1-$y);
return ($r,$g,$b) if wantarray;
return {R=>$r, G=>$g, B=>$b};
}
sub rgb_to_xyz {
return unless defined wantarray;
my ($x,$y,$z);
my ($r,$g,$b)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($r,$g,$b)=($_[0]->{R},$_[0]->{G},$_[0]->{B});
}else{
($r,$g,$b)=@_;
}
if($r<0 || $r>1
|| $g<0 || $g>1
|| $b<0 || $b>1){
die "bad input for rgb_to_xyz $r $g $b";
}
if ( $r > 0.04045 ) {$r = ( ( $r + 0.055 ) / 1.055 ) ** 2.4 }
else { $r = $r / 12.92 }
if ( $g > 0.04045 ) {$g = ( ( $g + 0.055 ) / 1.055 ) ** 2.4 }
else { $g = $g/ 12.92 }
if ( $b > 0.04045 ) {$b = ( ( $b + 0.055 ) / 1.055 ) ** 2.4 }
else { $b = $b / 12.92 }
$x = $r * 0.4124 + $g * 0.3576 + $b * 0.1805;
$y = $r * 0.2126 + $g * 0.7152 + $b * 0.0722;
$z = $r * 0.0193 + $g * 0.1192 + $b * 0.9505;
return ($x,$y,$z) if wantarray;
return {X=>$x, Y=>$y, Z=>$z};
}
sub xyz_to_rgb {
return unless defined wantarray;
my ($r,$g,$b);
my ($x,$y,$z)=(0,0,0);
if(ref $_[0] eq 'HASH'){
($x,$y,$z)=($_[0]->{X},$_[0]->{Y},$_[0]->{Z});
}else{
($x,$y,$z)=@_;
}
$r = $x * 3.2406 + $y * -1.5372 + $z * -0.4986;
$g = $x * -0.9689 + $y * 1.8758 + $z * 0.0415;
$b = $x * 0.0557 + $y * -0.2040 + $z * 1.0570;
if ( $r > 0.0031308 ) {$r = 1.055 * ( $r ** ( 1 / 2.4 ) ) - 0.055}
else {$r = 12.92 * $r}
if ( $g > 0.0031308 ) { $g = 1.055 * ( $g ** ( 1 / 2.4 ) ) - 0.055}
else { $g = 12.92 * $g }
if ( $b > 0.0031308 ) { $b = 1.055 * ( $b ** ( 1 / 2.4 ) ) - 0.055}
else { $b = 12.92 * $b }
return ($r,$g,$b) if wantarray;
return {R=>$r, G=>$g, B=>$b};
}
1;
__END__
=head1 NAME
col_space - Color space conversions
=head1 SYNOPSIS
# array i/o
my ($r,$g,$b)=hsv_to_rgb(0.5,1.0,0.3);
# scalar i/o
my $c=hsv_to_rgb({H => 0.5,S => 1.0,V => 0.3})
print $c->{H}.' '.$c->{S}.' '.$c->{V};
# array i/o can be mixed w/scalar i/o
rgb_to_hsl, hsl_to_rgb,
rgb_to_hsv, hsv_to_rgb,
rgb_to_cmy, cmy_to_rgb,
rgb_to_xyz, xyz_to_rgb
=head1 DEFINITIONS
=over 8
=item RGB
red - green - blue color space
=item HSL
hue - saturation - lightness color space
=item HSV
hue - saturation - value color space
=item CMY
cyan - magenta - yellow color space
=back
=head1 DESCRIPTION
Converts between HSL and RGB, HSV and RGB, CMY and RGB.
Three elements array or ref to hash input and output.
Input and output values are B<normalized to [0.0,1.0] range>. Input is
+ asserted.
=head1 AUTHOR
Author: Jacek S. (js29a@ceti.pl).
=cut
|