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. Input is asserted. =head1 AUTHOR Author: Jacek S. (js29a@ceti.pl). =cut