diff --git a/Changes b/Changes index 6fff241..687555a 100644 --- a/Changes +++ b/Changes @@ -150,7 +150,3 @@ * = initial release - moved code out of Chart module * \ created own distro * ~ small POD fixes - - - * * added color set method: bowl - * * added getter method: near_names diff --git a/lib/Graphics/Toolkit/Color/Space/Instance/LAB.pm b/lib/Graphics/Toolkit/Color/Space/Instance/LAB.pm index 8e3d0ca..486739d 100644 --- a/lib/Graphics/Toolkit/Color/Space/Instance/LAB.pm +++ b/lib/Graphics/Toolkit/Color/Space/Instance/LAB.pm @@ -14,41 +14,39 @@ my $lab_def = Graphics::Toolkit::Color::Space->new( prefix => 'CIE', $lab_def->add_converter('RGB', \&to_rgb, \&from_rgb ); -my @xyz_range = (0.95047, 1, 1.08883); +my @D65 = (0.95047, 1, 1.08883); # illuminant +my $eta = 0.008856 ; +my $kappa = 903.3 / 100; sub from_rgb { my ($r, $g, $b) = @{$_[0]}; - my ($x, $y, $z) = mult_matrix([[0.4124564, 0.2126729, 0.0193339], - [0.3575761, 0.7151522, 0.1191920], - [0.1804375, 0.0721750, 0.9503041]], - apply_d65( $r ), apply_d65( $g ), apply_d65( $b )); -say "x y z $x, $y, $z"; - $x /= 0.95047; - $z /= 0.108883; - - $x = ($x > 0.008856) ? ($x ** (1/3)) : (7.7870689 * $x + 0.137931034); - $y = ($y > 0.008856) ? ($y ** (1/3)) : (7.7870689 * $y + 0.137931034); - $z = ($z > 0.008856) ? ($z ** (1/3)) : (7.7870689 * $z + 0.137931034); - - return ((116 * $y) - 16, $a = 500 * ($x - $y), 200 * ($y - $z)); + my (@xyz) = mult_matrix([[0.4124564, 0.2126729, 0.0193339], + [0.3575761, 0.7151522, 0.1191920], + [0.1804375, 0.0721750, 0.9503041]], apply_d65($r), apply_d65($g), apply_d65($b)); + @xyz = map { $xyz[$_] / $D65[$_] } 0 .. 2; + @xyz = map { $_ > $eta ? ($_ ** (1/3)) : ((($kappa * $_) + .16) / 1.16) } @xyz; + + return ((1.16 * $xyz[1]) - .16, ($xyz[0] - $xyz[1] + 1) / 2, (($xyz[1] - $xyz[2] + 1) / 2)); # l a b } sub to_rgb { my ($l, $a, $b) = @{$_[0]}; - my $y = ($l + 16) / 116; - my $x = ($a / 500) + $y; - my $z = $y - ($b / 200); - $x = ($x**3 > 0.008856) ? ($x ** 3) : (($x - 0.137931034) / 7.7870689); - $y = ($y**3 > 0.008856) ? ($y ** 3) : (($y - 0.137931034) / 7.7870689); - $z = ($z**3 > 0.008856) ? ($z ** 3) : (($z - 0.137931034) / 7.7870689); - $x *= 0.95047; - $z *= 0.108883; - my ($r, $g, $bl) = mult_matrix([[ 3.2404542, -0.9692660, 0.0556434], - [-1.5371385, 1.8760108, -0.2040259], - [-0.4985314, 0.0415560, 1.0572252]], $x, $y, $z); - - return ( remove_d65($r), remove_d65($g), remove_d65($bl)); + my $y = ($l + .16) / 1.16; + my $x = $y + (($a * 2)-1); + my $z = $y - (($b * 2)-1); + + $x = ($x**3 > $eta) ? ($x ** 3) : ($kappa * (($x * 1.16) - .16)); + $y = ($y**3 > ($eta * $kappa)) ? ($y ** 3) : ($kappa * $l); + $z = ($z**3 > $eta) ? ($z ** 3) : ($kappa * (($z * 1.16) - .16)); + + $x *= $D65[0]; + $z *= $D65[2]; + my (@rgb) = mult_matrix([[ 3.2404542, -0.9692660, 0.0556434], + [-1.5371385, 1.8760108, -0.2040259], + [-0.4985314, 0.0415560, 1.0572252]], $x, $y, $z); + + return ( map { remove_d65($_) } @rgb ); } $lab_def; diff --git a/lib/Graphics/Toolkit/Color/Space/Instance/LUV.pm b/lib/Graphics/Toolkit/Color/Space/Instance/LUV.pm index a3ee277..35eec5c 100644 --- a/lib/Graphics/Toolkit/Color/Space/Instance/LUV.pm +++ b/lib/Graphics/Toolkit/Color/Space/Instance/LUV.pm @@ -9,26 +9,47 @@ use Graphics::Toolkit::Color::Space::Util qw/mult_matrix apply_d65 remove_d65/; # cyan-orange balance, magenta-green balance -my $luv_def = Graphics::Toolkit::Color::Space->new( axis => [qw/L* u* v*/], - prefix => 'CIE', - range => [1, 1, 1] ); +my $luv_def = Graphics::Toolkit::Color::Space->new( prefix => 'CIE', + axis => [qw/L* u* v*/], + range => [100, 1, 1] ); $luv_def->add_converter('RGB', \&to_rgb, \&from_rgb ); +my @D65 = (0.95047, 1, 1.08883); # illuminant +my $eta = 0.008856 ; +my $kappa = 903.3 / 100; + sub from_rgb { - my ($r, $g, $b) = @_; - return mult_matrix([[0.4124564, 0.2126729, 0.0193339], - [0.3575761, 0.7151522, 0.1191920], - [0.1804375, 0.0721750, 0.9503041]], apply_d65( $r ), apply_d65( $g ), apply_d65( $b )); + my ($r, $g, $b) = @{$_[0]}; + my (@xyz) = mult_matrix([[0.4124564, 0.2126729, 0.0193339], + [0.3575761, 0.7151522, 0.1191920], + [0.1804375, 0.0721750, 0.9503041]], apply_d65($r), apply_d65($g), apply_d65($b)); + @xyz = map { $xyz[$_] / $D65[$_] } 0 .. 2; + @xyz = map { $_ > $eta ? ($_ ** (1/3)) : ((($kappa * $_) + .16) / 1.16) } @xyz; + + return ((1.16 * $xyz[1]) - .16, ($xyz[0] - $xyz[1] + 1) / 2, (($xyz[1] - $xyz[2] + 1) / 2)); # l a b } -sub to_rgb { - my ($x, $y, $z) = @_; - my ($r, $g, $b) = mult_matrix([[ 3.2404542, -0.9692660, 0.0556434], - [-1.5371385, 1.8760108, -0.2040259], - [-0.4985314, 0.0415560, 1.0572252]], $x, $y, $z); - return ( remove_d65($r), remove_d65($g), remove_d65($b)); +sub to_rgb { + my ($l, $a, $b) = @{$_[0]}; + my $y = ($l + .16) / 1.16; + my $x = $y + (($a * 2)-1); + my $z = $y - (($b * 2)-1); + + $x = ($x**3 > $eta) ? ($x ** 3) : ($kappa * (($x * 1.16) - .16)); + $y = ($y**3 > ($eta * $kappa)) ? ($y ** 3) : ($kappa * $l); + $z = ($z**3 > $eta) ? ($z ** 3) : ($kappa * (($z * 1.16) - .16)); + + $x *= $D65[0]; + $z *= $D65[2]; + my (@rgb) = mult_matrix([[ 3.2404542, -0.9692660, 0.0556434], + [-1.5371385, 1.8760108, -0.2040259], + [-0.4985314, 0.0415560, 1.0572252]], $x, $y, $z); + + return ( map { remove_d65($_) } @rgb ); } $luv_def; + +#say "xyz r : @xyz"; diff --git a/lib/Graphics/Toolkit/Color/Space/Instance/XYZ.pm b/lib/Graphics/Toolkit/Color/Space/Instance/XYZ.pm index eda0cbb..1e40721 100644 --- a/lib/Graphics/Toolkit/Color/Space/Instance/XYZ.pm +++ b/lib/Graphics/Toolkit/Color/Space/Instance/XYZ.pm @@ -7,10 +7,10 @@ package Graphics::Toolkit::Color::Space::Instance::XYZ; use Graphics::Toolkit::Color::Space; use Graphics::Toolkit::Color::Space::Util qw/mult_matrix apply_d65 remove_d65/; -my @range = (0.95047, 1, 1.08883); +my @D65 = (0.95047, 1, 1.08883); my $xyz_def = Graphics::Toolkit::Color::Space->new( prefix => 'CIE', axis => [qw/X Y Z/], - range => [map {$range[$_] * 100} 0 .. 2], + range => [map {$D65[$_] * 100} 0 .. 2], precision => 3, ); $xyz_def->add_converter('RGB', \&to_rgb, \&from_rgb ); @@ -20,12 +20,12 @@ sub from_rgb { my (@xyz) = mult_matrix([[0.4124564, 0.2126729, 0.0193339], [0.3575761, 0.7151522, 0.1191920], [0.1804375, 0.0721750, 0.9503041]], apply_d65( $r ), apply_d65( $g ), apply_d65( $b )); - map {$xyz[$_] / $range[$_]} 0 .. 2; + map {$xyz[$_] / $D65[$_]} 0 .. 2; } sub to_rgb { my (@xyz) = @{$_[0]}; - @xyz = map { $xyz[$_] * $range[$_] } 0 .. 2; + @xyz = map { $xyz[$_] * $D65[$_] } 0 .. 2; my ($r, $g, $b) = mult_matrix([[ 3.2404542, -0.9692660, 0.0556434], [-1.5371385, 1.8760108, -0.2040259], [-0.4985314, 0.0415560, 1.0572252]], @xyz); diff --git a/lib/Graphics/Toolkit/Color/Space/Util.pm b/lib/Graphics/Toolkit/Color/Space/Util.pm index 193b121..596fc6d 100644 --- a/lib/Graphics/Toolkit/Color/Space/Util.pm +++ b/lib/Graphics/Toolkit/Color/Space/Util.pm @@ -59,5 +59,3 @@ sub mult_matrix { 1; - -# min(floor(val*256),255) diff --git a/t/22_lab_space.t b/t/22_lab_space.t index 3cd2f93..74d5297 100644 --- a/t/22_lab_space.t +++ b/t/22_lab_space.t @@ -2,13 +2,14 @@ use v5.12; use warnings; -use Test::More tests => 57; +use Test::More tests => 92; -BEGIN { unshift @INC, 'lib', '../lib'} +BEGIN { unshift @INC, 'lib', '../lib', 't/lib'} my $module = 'Graphics::Toolkit::Color::Space::Instance::LAB'; my $space = eval "require $module"; use Graphics::Toolkit::Color::Space::Util ':all'; +#use Test::Color ':all'; is( not($@), 1, 'could load the module'); is( ref $space, 'Graphics::Toolkit::Color::Space', 'got tight return value by loading module'); @@ -44,69 +45,96 @@ is( $val->[2], -0.1, 'third value good'); is( $space->format([0,1,0], 'css_string'), 'cielab(0, 1, 0)', 'can format css string'); $val = $space->deconvert( [ 0, 0, 0], 'RGB'); -is( ref $val, 'ARRAY', 'deconverted tuple of zeros'); +is( ref $val, 'ARRAY', 'deconverted tuple of zeros (black)'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , 0), 1, 'first value good'); +is( close_enough( $val->[1] , 0.5), 1, 'second value good'); +is( close_enough( $val->[2] , 0.5), 1, 'third value good'); + +$val = $space->denormalize( [0, .5, .5] ); +is( ref $val, 'ARRAY', 'denormalized deconverted tuple of zeros'); is( int @$val, 3, 'right amount of values'); is( close_enough( $val->[0] , 0), 1, 'first value good'); is( close_enough( $val->[1] , 0), 1, 'second value good'); is( close_enough( $val->[2] , 0), 1, 'third value good'); -$val = $space->convert( [ 0, 0, 0], 'RGB'); -is( ref $val, 'ARRAY', 'converted tuple of zeros'); -is( int @$val, 3, 'right amount of values'); +$val = $space->normalize( [0, 0, 0] ); +is( ref $val, 'ARRAY', 'normalized tuple of zeros'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , 0), 1, 'first value good'); +is( close_enough( $val->[1] , 0.5), 1, 'second value good'); +is( close_enough( $val->[2] , 0.5), 1, 'third value good'); + +$val = $space->convert( [ 0, 0.5, 0.5], 'RGB'); +is( ref $val, 'ARRAY', 'converted white to RGB'); +is( int @$val, 3, 'right amount of values'); is( close_enough( $val->[0] , 0), 1, 'first value good'); is( close_enough( $val->[1] , 0), 1, 'second value good'); is( close_enough( $val->[2] , 0), 1, 'third value good'); $val = $space->deconvert( [ 1, 1, 1,], 'RGB'); -is( ref $val, 'ARRAY', 'deconverted tuple of zeros'); +is( ref $val, 'ARRAY', 'deconverted tuple of ones (white)'); +is( int @$val, 3, 'right amount of values'); +is( close_enough($val->[0], 1), 1, 'first value good'); +is( close_enough($val->[1], 0.5), 1, 'second value good'); +is( close_enough($val->[2], 0.5), 1, 'third value good'); + +$val = $space->convert( [ 1, 0.5, 0.5], 'RGB'); +is( ref $val, 'ARRAY', 'converted tuple of zeros'); is( int @$val, 3, 'right amount of values'); is( close_enough( $val->[0] , 1), 1, 'first value good'); +is( close_enough( $val->[1] , 1), 1, 'second value good'); +is( close_enough( $val->[2] , 1), 1, 'third value good'); + +$val = $space->deconvert( [ 0.5, 0.5, 0.5], 'RGB'); +is( ref $val, 'ARRAY', 'converted gray to RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , .53389), 1, 'first value good'); +is( close_enough( $val->[1] , .5), 1, 'second value good'); +is( close_enough( $val->[2] , .5), 1, 'third value good'); + +$val = $space->denormalize( [0.53389, .5, .5] ); +is( ref $val, 'ARRAY', 'denormalized deconverted gray'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , 53.389), 1, 'first value good'); is( close_enough( $val->[1] , 0), 1, 'second value good'); -is( $val->[2], 1, 'third value good'); - - -exit 0; +is( close_enough( $val->[2] , 0), 1, 'third value good'); -my @xyz = $space->deconvert( [ 0.5, 0.5, 0.5], 'RGB'); -is( int @xyz, 3, 'converted color grey has three XYZ values'); -is( close_enough($xyz[0], 0.20344), 1, 'converted color grey has computed right X value'); -is( close_enough($xyz[1], 0.21404), 1, 'converted color grey has computed right Y value'); -is( close_enough($xyz[2], 0.23305), 1, 'converted color grey has computed right Z value'); - -@xyz = $space->deconvert( [ 1, 1, 1], 'RGB'); -is( int @xyz, 3, 'converted color white has three XYZ values'); -is( close_enough($xyz[0], 0.95047), 1, 'converted color white has computed right X value'); -is( close_enough($xyz[1], 1), 1, 'converted color white has computed right Y value'); -is( close_enough($xyz[2], 1.08883), 1, 'converted color white has computed right Z value'); - -@xyz = $space->deconvert( [ 1, 0, 0.5], 'RGB'); -is( int @xyz, 3, 'converted color pink has three XYZ values'); -is( close_enough($xyz[0], 0.45108), 1, 'converted color pink has computed right X value'); -is( close_enough($xyz[1], 0.22821), 1, 'converted color pink has computed right Y value'); -is( close_enough($xyz[2], 0.22274), 1, 'converted color pink has computed right Z value'); - -my @rgb = $space->convert( [0, 0, 0], 'RGB'); -is( int @rgb, 3, 'converted back black with 3 values'); -is( close_enough($rgb[0], 0), 1, 'right red value'); -is( close_enough($rgb[1], 0), 1, 'right green value'); -is( close_enough($rgb[2], 0), 1, 'right blue value'); - -@rgb = $space->convert( [0.20344, 0.21404, 0.23305], 'RGB'); -is( int @rgb, 3, 'converted back gray with 3 values'); -is( close_enough($rgb[0], 0.5), 1, 'right red value'); -is( close_enough($rgb[1], 0.5), 1, 'right green value'); -is( close_enough($rgb[2], 0.5), 1, 'right blue value'); - -@rgb = $space->convert( [0.95047, 1, 1.08883], 'RGB'); -is( int @rgb, 3, 'converted back gray with 3 values'); -is( close_enough($rgb[0], 1), 1, 'right red value'); -is( close_enough($rgb[1], 1), 1, 'right green value'); -is( close_enough($rgb[2], 1), 1, 'right blue value'); - -@rgb = $space->convert( [0.45108, 0.22821, 0.22274], 'RGB'); -is( int @rgb, 3, 'converted back gray with 3 values'); -is( close_enough($rgb[0], 1 ), 1, 'right red value'); -is( close_enough($rgb[1], 0 ), 1, 'right green value'); -is( close_enough($rgb[2], 0.5), 1, 'right blue value'); +$val = $space->convert( [ 0.53389, .5, .5], 'RGB'); +is( ref $val, 'ARRAY', 'converted back gray to RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , .5), 1, 'first value good'); +is( close_enough( $val->[1] , .5), 1, 'second value good'); +is( close_enough( $val->[2] , .5), 1, 'third value good'); + + +$val = $space->deconvert( [ 1, 0, 0.5], 'RGB'); +is( ref $val, 'ARRAY', 'converted purple from RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , .54878), 1, 'first value good'); +is( close_enough( $val->[1] , .584499), 1, 'second value good'); +is( close_enough( $val->[2] , .5109), 1, 'third value good'); + +$val = $space->convert( [ 0.54878, .584499, .5109], 'RGB'); +is( ref $val, 'ARRAY', 'converted back gray to RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , 1), 1, 'first value good'); +is( close_enough( $val->[1] , 0), 1, 'second value good'); +is( close_enough( $val->[2] , .5), 1, 'third value good'); + + +$val = $space->deconvert( [ .1, 0.2, 0.9], 'RGB'); +is( ref $val, 'ARRAY', 'converted BLUE from RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , .34526), 1, 'first value good'); +is( close_enough( $val->[1] , .557165), 1, 'second value good'); +is( close_enough( $val->[2] , .2757375),1, 'third value good'); + +$val = $space->convert( [ 0.34526, .557165, .2757375], 'RGB'); +is( ref $val, 'ARRAY', 'converted back BLUE to RGB'); +is( int @$val, 3, 'right amount of values'); +is( close_enough( $val->[0] , .1), 1, 'first value good'); +is( close_enough( $val->[1] , .2), 1, 'second value good'); +is( close_enough( $val->[2] , .9), 1, 'third value good'); exit 0; diff --git a/t/23_luv_space.t b/t/23_luv_space.t index fbe53e4..8beb135 100644 --- a/t/23_luv_space.t +++ b/t/23_luv_space.t @@ -5,21 +5,21 @@ use warnings; use Test::More tests => 40; BEGIN { unshift @INC, 'lib', '../lib'} -my $module = 'Graphics::Toolkit::Color::Space::Instance::YIQ'; +my $module = 'Graphics::Toolkit::Color::Space::Instance::LUV'; my $def = eval "require $module"; use Graphics::Toolkit::Color::Space::Util ':all'; is( not($@), 1, 'could load the module'); is( ref $def, 'Graphics::Toolkit::Color::Space', 'got tight return value by loading module'); -is( $def->name, 'YIQ', 'color space has right name'); -is( $def->dimensions, 3, 'color space has 3 dimensions'); +is( $def->name, 'CIELUV', 'color space has right name'); +is( $def->axis, 3, 'color space has 3 dimensions'); is( $def->is_array([0,0,0]), 1, 'vector has 3 elements'); is( $def->is_partial_hash({i => 1, quadrature => 0}), 1, 'found hash with some keys'); is( $def->can_convert('rgb'), 1, 'do only convert from and to rgb'); is( $def->can_convert('yiq'), 0, 'can not convert to itself'); is( $def->format([0,0,0], 'css_string'), 'yiq(0,0,0)', 'can format css string'); -my @val = $def->deformat(['YIQ', 1, 0, -0.1]); +my @val = $def->deformat(['LUV', 1, 0, -0.1]); is( int @val, 3, 'deformated value triplet (vector)'); is( $val[0], 1, 'first value good'); is( $val[1], 0, 'second value good'); diff --git a/t/lib/Test/Color.pm b/t/lib/Test/Color.pm new file mode 100644 index 0000000..262af0d --- /dev/null +++ b/t/lib/Test/Color.pm @@ -0,0 +1,19 @@ +use v5.12; +use warnings; + +# utilities for any sub module of the distribution + +package Test::Color; + +use Exporter 'import'; +our @EXPORT_OK = qw/close_enough/; +our %EXPORT_TAGS = (all => [@EXPORT_OK]); + +my $half = 0.50000000000008; +my $tolerance = 0.00000000000008; + + +sub close_enough { abs($_[0] - $_[1]) < 0.008 if defined $_[1]} + + +1;