File Coverage

blib/lib/Graphics/ColorObject.pm
Criterion Covered Total %
statement 553 904 61.1
branch 120 204 58.8
condition 18 36 50.0
subroutine 89 150 59.3
pod 47 127 37.0
total 827 1421 58.2


line stmt bran cond sub pod time code
1             package Graphics::ColorObject;
2              
3             # Copyright 2003-2005 by Alex Izvorski
4              
5             # Portions Copyright 2001-2003 by Alfred Reibenschuh
6              
7             # $Id: ColorObject.pm,v 1.12 2005/07/19 10:11:47 ai Exp $
8              
9             =head1 NAME
10              
11             Graphics::ColorObject - convert between color spaces
12              
13              
14             =head1 SYNOPSIS
15              
16             use Graphics::ColorObject;
17            
18             # rgb to hsv
19             $color = Graphics::ColorObject->new_RGB([$r, $g, $b]);
20             ($h, $s, $v) = @{ $color->as_HSV() };
21            
22             # one rgb space to another (NTSC to PAL)
23             $color = Graphics::ColorObject->new_RGB([$r, $g, $b], space=>'NTSC');
24             ($r, $g, $b) = @{ $color->as_RGB(space=>'PAL') };
25              
26              
27             =head1 ABSTRACT
28              
29             Use this module to convert between all the common color spaces. As a pure Perl module, it is not very fast, and so it you want to convert entire images quickly, this is probably not what you want. The emphasis is on completeness and accurate conversion.
30              
31             Supported color spaces are: RGB (including sRGB, Apple, Adobe, CIE Rec 601, CIE Rec 709, CIE ITU, and about a dozen other RGB spaces), CMY, CMYK, HSL, HSV, XYZ, xyY, Lab, LCHab, Luv, LCHuv, YPbPr, YCbCr, YUV, YIQ, PhotoYCC.
32              
33             Conversion between different RGB working spaces, and between different white-points, is fully supported.
34              
35              
36             =head1 DESCRIPTION
37              
38             For any supported color space XXX, there is one constructor new_XXX that creates a color using data in that color space, and one method as_XXX that returns the current color as expressed in that color space. For example, for RGB there is new_RGB and as_RGB. The color data is always passed as an array reference to a three-element array (four-element in the case of CMYK). Thus, to convert from RGB to HSL, you can use:
39              
40             $color = Graphics::ColorObject->new_RGB([$r, $g, $b]);
41             ($h, $s, $l) = @{ $color->as_HSL() };
42              
43             The constructor can always take a hash of optional arguments in addition to the color value, namely the working RGB space and the white point. For example:
44              
45             $color = Graphics::ColorObject->new_RGB([$r, $g, $b], space=>'Adobe', white_point=>'D65');
46              
47             For a list of all supported color spaces, call Graphics::ColorObject->list_colorspaces(). For a list of all RGB working spaces and of all white points that this module supports, call Graphics::ColorObject->list_rgb_spaces() and Graphics::ColorObject->list_white_points().
48              
49             If not specified, the working RGB space will be sRGB. Many non-RGB conversions also rely on an implicit RGB space, and passing an RGB space as an option (either to the constructor or later) will have an effect on the values.
50              
51              
52             =head1 VARIOUS NOTES AND GOTCHAS
53              
54             Most conversions will return out-of-gamut values if necessary, because that way they are lossless and can be chained in calculations, or reversed to produce the original values. Many conversion methods will take an optional boolean "clip" parameter to restrict the returned values to be within gamut:
55              
56             ($r, $g, $b) = @{ $color->as_RGB(space=>'sRGB', clip=>1) };
57              
58             Currently clipping is supported in RGB, RGB-derived (HSL, CMY) and chroma-luma separated (YUV, etc) spaces, but not in XYZ-derived spaces. The only way to check whether a value is within gamut is to convert it with and without the clip option and compare the two results. An RGB value is within gamut simply if R, G and B are between 0 and 1, but other spaces can be much harder to check.
59              
60             RGB values are non-linear (gamma-adjusted) floating-point values scaled in the range from 0 to 1. If you want integer values in the range 0..255, use the new_RGB255/as_RGB255 functions instead. If you want linear RGB (not gamma-adjusted) use RGB_to_linear_RGB([$r, $g, $b]).
61              
62             Functions that use an angle value always express it in degrees from 0 to 360. That includes the hue H in HSL, HSV, LCHab and LCHuv. Use rad2deg and deg2rad from Math::Trig to convert to/from degrees if necessary.
63              
64             There is some confusion in the naming of YUV and related (Y-something-something) colorspaces. Most of the time when "YUV" or "YCC" is used in software, for example in JPEG and MPEG2, that is actually YCbCr, a chroma-luma separated space with integer values of Y in the range [16..235], Cb and Cr in [16..240]. JPEG uses a modified YCbCr with values in [0..255] (which is not implemented in this module). As used here, YUV is a floating-point representation of the analog signal in PAL TV, YIQ is the same for NTSC TV, YPbPr is component analog video, and PhotoYCC or YCC is the Kodak PhotoCD standard.
65              
66             The set_white_point() function can take arbitrary temperatures as well as the predefined standard illuminants. The valid range of temperatures is from 4000K to 25000K.
67              
68              
69             =head1 RECOMMENDATIONS
70              
71             Aside from converting from one space to another, what colorspace is the best one to use for a particular task? This section attempts to answer that question.
72              
73             For "generic" RGB values, use sRGB (which is the default).
74              
75             For 2D effects filters, use Lab (or LCHab).
76              
77             For adjustment of brightness, saturation and hue, use LCHab or LSHab.
78              
79             For compression, use YCbCr, or use YPbPr and convert to integer values in a way that makes sense in your application.
80              
81             For representing data as colors, use Lab (straight lines between points in Lab are more-or-less uniform gradients, unlike straight lines in RGB, for example).
82              
83              
84             =head1 UPGRADING FROM 0.3a2 AND OLDER VERSIONS
85              
86             Version 0.4 and later are a complete rewrite from the previous major version, 0.3a2. The API is completely changed. The old API should be emulated exactly in all cases. Please test any code that uses this module when upgrading. If you encounter any strange behavior, please downgrade to 0.3a2 and email me a bug report. Additionally, the exact values returned by some functions may be slightly different, this is not a bug - the new values are (more) correct.
87              
88              
89             =head1 METHODS
90              
91             =cut
92              
93 7     7   192160 use 5.006;
  7         27  
  7         281  
94 7     7   44 use strict;
  7         12  
  7         237  
95 7     7   37 use warnings;
  7         14  
  7         1119  
96              
97             require Exporter;
98              
99             our @ISA = qw(Exporter);
100              
101             our %EXPORT_TAGS = ( 'all' => [ qw(
102             RGB_to_RGB255
103             RGB255_to_RGB
104             RGBhex_to_RGB
105             RGB_to_RGBhex
106             RGB_to_XYZ
107             XYZ_to_RGB
108             XYZ_to_Lab
109             Lab_to_XYZ
110             RGB_to_Lab
111             Lab_to_RGB
112             XYZ_to_Luv
113             Luv_to_XYZ
114             Luv_to_LCHuv
115             LCHuv_to_Luv
116             XYZ_to_xyY
117             xyY_to_XYZ
118             Lab_to_LCHab
119             LCHab_to_Lab
120             RGB_to_linear_RGB
121             linear_RGB_to_RGB
122             RGB_to_YPbPr
123             YPbPr_to_RGB
124             RGB_to_HSV
125             HSV_to_RGB
126             RGB_to_HSL
127             HSL_to_RGB
128             ) ] );
129              
130             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
131              
132             our @EXPORT = qw();
133              
134             our $VERSION = '0.5.0';
135              
136 7     7   44 use Carp;
  7         13  
  7         800  
137 7     7   6852 use POSIX qw(pow);
  7         58528  
  7         46  
138 7     7   16535 use Math::Trig;
  7         196763  
  7         1460  
139              
140             ############ OO interface ##############
141              
142 7     7   79 use vars qw(%RGB_SPACES %WHITE_POINTS %COLORNAMES);
  7         17  
  7         81246  
143              
144             sub new
145             {
146 51609     51609 1 117285 my ($pkgname, @opts) = @_;
147              
148 51609         99749 my $this = +{};
149 51609         126247 bless $this, $pkgname;
150 51609         123933 my $col = &Graphics::ColorObject::namecolor($opts[0]);
151 51609 50       178664 if ($col)
152             {
153 0         0 shift(@opts);
154 0         0 $this = new_RGB($pkgname, $col, @opts);
155 0         0 return $this;
156             }
157            
158             # check before converting to hash, even if the extra args are bogus at least it won't generate an error
159 51609 50       154807 if (scalar(@opts) % 2 == 0)
160             {
161 51609         125331 my %opts = @opts;
162 51609         146543 $this->{space} = $opts{space};
163 51609         148873 $this->{white_point} = $opts{white_point};
164             }
165              
166 51609         133791 return $this;
167             }
168              
169             =head2 $color = Graphics::ColorObject->new_XYZ([$X, $Y, $Z])
170             =cut
171              
172             sub new_XYZ
173             {
174 1083     1083 1 82640 my ($pkgname, $xyz, %opts) = @_;
175 1083         2928 my $this = &new($pkgname, %opts);
176 1083         2091 $this->{xyz} = $xyz;
177 1083         4145 return $this;
178             }
179              
180             =head2 $color = Graphics::ColorObject->new_xyY([$x, $y, $Y])
181             =cut
182              
183             sub new_xyY
184             {
185 1080     1080 1 112173 my ($pkgname, $xyy, %opts) = @_;
186 1080         3193 my $this = &new($pkgname, %opts);
187 1080         4435 $this->{xyz} = &xyY_to_XYZ($xyy);
188 1080         5332 return $this;
189             }
190              
191             =head2 $color = Graphics::ColorObject->new_RGB([$R, $G, $B])
192             =cut
193              
194             sub new_RGB
195             {
196 45125     45125 1 7697350 my ($pkgname, $rgb, %opts) = @_;
197 45125         148391 my $this = &new($pkgname, %opts);
198 45125         120825 $this->{xyz} = &RGB_to_XYZ($rgb, $this->{space});
199 45125         267644 return $this;
200             }
201              
202             =head2 $color = Graphics::ColorObject->new_RGB255([$R, $G, $B])
203             =cut
204              
205             sub new_RGB255
206             {
207 0     0 1 0 my ($pkgname, $rgb255, %opts) = @_;
208 0         0 return &new_RGB($pkgname, &RGB255_to_RGB($rgb255), %opts);
209             }
210              
211             =head2 $color = Graphics::ColorObject->new_RGBhex($rgbhex)
212             =cut
213              
214             sub new_RGBhex
215             {
216 3     3 1 26 my ($pkgname, $rgbhex, %opts) = @_;
217 3         9 return &new_RGB($pkgname, &RGBhex_to_RGB($rgbhex), %opts);
218             }
219              
220             =head2 $color = Graphics::ColorObject->new_Lab([$L, $a, $b])
221             =cut
222              
223             sub new_Lab
224             {
225 1081     1081 1 77105 my ($pkgname, $lab, %opts) = @_;
226 1081         3006 my $this = &new($pkgname, %opts);
227 1081         2738 $this->{xyz} = &Lab_to_XYZ($lab, $this->get_XYZ_white());
228 1081         6198 return $this;
229             }
230              
231             =head2 $color = Graphics::ColorObject->new_LCHab([$L, $C, $H])
232             =cut
233              
234             sub new_LCHab
235             {
236 1080     1080 1 98679 my ($pkgname, $lch, %opts) = @_;
237 1080         2782 my $this = &new($pkgname, %opts);
238 1080         2446 $this->{xyz} = &Lab_to_XYZ(&LCHab_to_Lab($lch), $this->get_XYZ_white());
239 1080         6234 return $this;
240             }
241              
242             =head2 $color = Graphics::ColorObject->new_Luv([$L, $u, $v])
243             =cut
244              
245             sub new_Luv
246             {
247 1080     1080 1 84543 my ($pkgname, $luv, %opts) = @_;
248 1080         3234 my $this = &new($pkgname, %opts);
249 1080         3193 $this->{xyz} = &Luv_to_XYZ($luv, $this->get_XYZ_white());
250 1080         5910 return $this;
251             }
252              
253             =head2 $color = Graphics::ColorObject->new_LCHuv([$L, $C, $H])
254             =cut
255              
256             sub new_LCHuv
257             {
258 1080     1080 1 92732 my ($pkgname, $lch, %opts) = @_;
259 1080         3201 my $this = &new($pkgname, %opts);
260 1080         2686 $this->{xyz} = &Luv_to_XYZ(&LCHuv_to_Luv($lch), $this->get_XYZ_white());
261 1080         6343 return $this;
262             }
263              
264             =head2 $color = Graphics::ColorObject->new_HSL([$H, $S, $L])
265             =cut
266              
267             sub new_HSL
268             {
269 1080     1080 1 78576 my ($pkgname, $hsl, %opts) = @_;
270 1080         2404 return &new_RGB($pkgname, &HSL_to_RGB($hsl), %opts);
271             }
272              
273             =head2 $color = Graphics::ColorObject->new_HSV([$H, $S, $V])
274             =cut
275              
276             sub new_HSV
277             {
278 1080     1080 1 79632 my ($pkgname, $hsv, %opts) = @_;
279 1080         2337 return &new_RGB($pkgname, &HSV_to_RGB($hsv), %opts);
280             }
281              
282             =head2 $color = Graphics::ColorObject->new_CMY([$C, $M, $Y])
283             =cut
284              
285             sub new_CMY
286             {
287 1080     1080 1 76740 my ($pkgname, $cmy, %opts) = @_;
288 1080         2142 return &new_RGB($pkgname, &CMY_to_RGB($cmy), %opts);
289             }
290              
291             =head2 $color = Graphics::ColorObject->new_CMYK([$C, $M, $Y])
292             =cut
293              
294             sub new_CMYK
295             {
296 1080     1080 1 88824 my ($pkgname, $cmyk, %opts) = @_;
297 1080         2404 return &new_RGB($pkgname, &CMY_to_RGB(&CMYK_to_CMY($cmyk)), %opts);
298             }
299              
300             =head2 $color = Graphics::ColorObject->new_YPbPr([$Y, $Pb, $Pr])
301             =cut
302              
303             sub new_YPbPr
304             {
305 1080     1080 1 93666 my ($pkgname, $ypbpr, %opts) = @_;
306 1080         3161 return &new_RGB($pkgname, &YPbPr_to_RGB($ypbpr), space => 'NTSC'); # force NTSC
307             }
308              
309             =head2 $color = Graphics::ColorObject->new_YCbCr([$Y, $Cb, $Cr])
310             =cut
311              
312             sub new_YCbCr
313             {
314 1080     1080 1 103761 my ($pkgname, $ycbcr, %opts) = @_;
315 1080         2431 return &new_RGB($pkgname, &YCbCr_to_RGB($ycbcr), space => 'NTSC'); # force NTSC
316             }
317              
318             =head2 $color = Graphics::ColorObject->new_YUV([$Y, $Cb, $Cr])
319             =cut
320              
321             sub new_YUV
322             {
323 1080     1080 1 88536 my ($pkgname, $yuv, %opts) = @_;
324 1080         3398 return &new_RGB($pkgname, &YUV_to_RGB($yuv), space => 'NTSC'); # force NTSC
325             }
326              
327             =head2 $color = Graphics::ColorObject->new_YIQ([$Y, $I, $Q])
328             =cut
329              
330             sub new_YIQ
331             {
332 1080     1080 1 79973 my ($pkgname, $yiq, %opts) = @_;
333 1080         2391 return &new_RGB($pkgname, &YIQ_to_RGB($yiq), space => 'NTSC'); # force NTSC
334             }
335              
336             =head2 $color = Graphics::ColorObject->new_PhotoYCC([$Y, $C1, $C2])
337             =cut
338              
339             sub new_PhotoYCC
340             {
341 0     0 1 0 my ($pkgname, $ycc, %opts) = @_;
342 0         0 return &new_RGB($pkgname, &PhotoYCC_to_RGB($ycc), space => 'sRGB'); # force sRGB
343             }
344              
345             =head2 ($X, $Y, $Z) = @{ $color->as_XYZ() }
346             =cut
347              
348             sub as_XYZ
349             {
350 1085     1085 1 13341 my ($this, %opts) = @_;
351 1085         1618 my $xyz = $this->{xyz};
352 1085 50       2847 if ($opts{clip})
353             {
354             # TODO check this is correct
355 0         0 my ($Xw, $Yw, $Zw) = @{ $this->get_XYZ_white() };
  0         0  
356 0         0 $xyz = &_generic_clip($xyz, [[0,$Xw], [0,$Yw], [0,$Zw]]);
357             }
358 1085         9281 return $xyz;
359             }
360              
361             =head2 ($R, $G, $B) = @{ $color->as_RGB() }
362             =cut
363              
364             sub as_RGB
365             {
366 45122     45122 1 129805 my ($this, %opts) = @_;
367 45122   66     137341 my $space = $opts{space} || $this->{space};
368 45122         104209 my $rgb = &XYZ_to_RGB($this->{xyz}, $space);
369 45122 50       116362 if ($opts{clip}) { $rgb = &_generic_clip($rgb, [[0,1], [0,1], [0,1]]); };
  0         0  
370 45122         408459 return $rgb;
371             }
372              
373             =head2 ($R, $G, $B) = @{ $color->as_RGB255() }
374             =cut
375              
376             sub as_RGB255
377             {
378 0     0 1 0 my ($this) = @_;
379             # always clipped
380 0         0 return &RGB_to_RGB255($this->as_RGB());
381             }
382              
383             =head2 $hex = $color->as_RGBhex()
384             =cut
385              
386             sub as_RGBhex
387             {
388 1     1 1 3 my ($this) = @_;
389             # always clipped
390 1         5 return &RGB_to_RGBhex($this->as_RGB());
391             }
392              
393             =head2 ($x, $y, $Y) = @{ $color->as_xyY() }
394             =cut
395              
396             sub as_xyY
397             {
398 1080     1080 1 2946 my ($this, %opts) = @_;
399 1080         3453 my $xyy = &XYZ_to_xyY($this->{xyz}, $this->get_XYZ_white());
400 1080         10561 return $xyy;
401             }
402              
403             =head2 ($L, $a, $b) = @{ $color->as_Lab() }
404             =cut
405              
406             sub as_Lab
407             {
408 1081     1081 1 1783 my ($this) = @_;
409 1081         4535 my $lab = &XYZ_to_Lab($this->{xyz}, $this->get_XYZ_white());
410 1081         10572 return $lab;
411             }
412              
413             =head2 ($L, $C, $H) = @{ $color->as_LCHab() }
414             =cut
415              
416             sub as_LCHab
417             {
418 1081     1081 1 1561 my ($this) = @_;
419 1081         2760 my $lchab = &Lab_to_LCHab( &XYZ_to_Lab($this->{xyz}, $this->get_XYZ_white()) );
420 1081         11436 return $lchab;
421             }
422              
423             =head2 ($L, $u, $v) = @{ $color->as_Luv() }
424             =cut
425              
426             sub as_Luv
427             {
428 1080     1080 1 2018 my ($this) = @_;
429 1080         2979 my $luv = &XYZ_to_Luv($this->{xyz}, $this->get_XYZ_white());
430 1080         11555 return $luv;
431             }
432              
433             =head2 ($L, $C, $H) = @{ $color->as_LCHuv() }
434             =cut
435              
436             sub as_LCHuv
437             {
438 1080     1080 1 1616 my ($this) = @_;
439 1080         3021 my $lchuv = &Luv_to_LCHuv( &XYZ_to_Luv($this->{xyz}, $this->get_XYZ_white()) );
440 1080         11947 return $lchuv;
441             }
442              
443             =head2 ($H, $S, $L) = @{ $color->as_HSL() }
444             =cut
445              
446             sub as_HSL
447             {
448 1080     1080 1 2931 my ($this, %opts) = @_;
449 1080         2646 my $hsl = &RGB_to_HSL( $this->as_RGB() );
450 1080 50       9759 if ($opts{clip}) { $hsl = &_generic_clip($hsl, [[0,360], [0,1], [0,1]]); };
  0         0  
451 1080         10371 return $hsl;
452             }
453              
454             =head2 ($H, $S, $V) = @{ $color->as_HSV() }
455             =cut
456              
457             sub as_HSV
458             {
459 1080     1080 1 3426 my ($this, %opts) = @_;
460 1080         2290 my $hsv = &RGB_to_HSV( $this->as_RGB() );
461 1080 50       13012 if ($opts{clip}) { $hsv = &_generic_clip($hsv, [[0,360], [0,1], [0,1]]); };
  0         0  
462 1080         11681 return $hsv;
463             }
464              
465             =head2 ($C, $M, $Y) = @{ $color->as_CMY() }
466             =cut
467              
468             sub as_CMY
469             {
470 1080     1080 1 2756 my ($this, %opts) = @_;
471 1080         2164 my $cmy = &RGB_to_CMY( $this->as_RGB() );
472 1080 50       3148 if ($opts{clip}) { $cmy = &_generic_clip($cmy, [[0,1], [0,1], [0,1]]); };
  0         0  
473 1080         9618 return $cmy;
474             }
475              
476             =head2 ($C, $M, $Y, $K) = @{ $color->as_CMYK() }
477             =cut
478              
479             sub as_CMYK
480             {
481 1080     1080 1 1637 my ($this) = @_;
482 1080         2316 my $cmyk = &CMY_to_CMYK( &RGB_to_CMY( $this->as_RGB() ) );
483             # TODO clip
484 1080         10324 return $cmyk;
485             }
486              
487             =head2 ($Y, $Pb, $Pr) = @{ $color->as_YPbPr() }
488             =cut
489              
490             sub as_YPbPr
491             {
492 1080     1080 1 2761 my ($this, %opts) = @_;
493 1080         2684 my $ypbpr = &RGB_to_YPbPr( $this->as_RGB( space => 'NTSC' ) );
494 1080 50       2939 if ($opts{clip}) { $ypbpr = &_generic_clip($ypbpr, [[0,1], [-0.5,0.5], [-0.5,0.5]]); };
  0         0  
495 1080         11627 return $ypbpr;
496             }
497              
498             =head2 ($Y, $Cb, $Cr) = @{ $color->as_YCbCr() }
499             =cut
500              
501             sub as_YCbCr
502             {
503 1080     1080 1 2702 my ($this, %opts) = @_;
504 1080         3114 my $ycbcr = &RGB_to_YCbCr( $this->as_RGB( space => 'NTSC' ) );
505 1080 50       3414 if ($opts{clip}) { $ycbcr = &_generic_clip($ycbcr, [[16,235], [16,239], [16,239]]); };
  0         0  
506             # TODO round to integers
507 1080         10401 return $ycbcr;
508             }
509              
510             =head2 ($Y, $U, $V) = @{ $color->as_YUV() }
511             =cut
512              
513             sub as_YUV
514             {
515 1080     1080 1 2934 my ($this) = @_;
516 1080         3171 my $yuv = &RGB_to_YUV( $this->as_RGB( space => 'NTSC' ) );
517 1080         12435 return $yuv;
518             }
519              
520             =head2 ($Y, $I, $Q) = @{ $color->as_YIQ() }
521             =cut
522              
523             sub as_YIQ
524             {
525 1080     1080 1 1825 my ($this) = @_;
526 1080         2670 my $yiq = &RGB_to_YIQ( $this->as_RGB( space => 'NTSC' ) );
527 1080         10695 return $yiq;
528             }
529              
530             =head2 ($Y, $C1, $C2) = @{ $color->as_PhotoYCC() }
531             =cut
532              
533             sub as_PhotoYCC
534             {
535 0     0 1 0 my ($this) = @_;
536 0         0 my $ycc = &RGB_to_PhotoYCC( $this->as_RGB( space => 'sRGB' ) );
537 0         0 return $ycc;
538             }
539              
540             # returns the XYZ value of the white point actually used (always defined, default is D65)
541             sub get_XYZ_white
542             {
543 9723     9723 0 45846 my ($this, %opts) = @_;
544 9723   66     87381 my $white_point = $opts{white_point} || $this->{white_point} ||
545             &_get_RGB_space_by_name( $opts{space} || $this->{space} )->{white_point};
546              
547 9723         22258 $white_point = &_check_white_point($white_point);
548              
549 9723         17056 my $xy = $WHITE_POINTS{ $white_point };
550              
551 9723         9989 my ($x, $y) = @{ $xy };
  9723         29784  
552 9723         36123 return &xyY_to_XYZ([$x, $y, 1.0]);
553             #return &RGB_to_XYZ([1, 1, 1], $this->{space});
554             }
555              
556             =head2 $white_point = $color->get_white_point()
557             Returns the name of the current white point. Value is one of the entries returned from list_white_points, such as "D65", or a color temperature.
558             =cut
559              
560             # returns the name of the white point actually used
561             # FIXME should be always defined
562             sub get_white_point
563             {
564 0     0 1 0 my ($this) = @_;
565 0         0 return $this->{white_point};
566             }
567              
568             =head2 $color->set_white_point("D65")
569             Sets the current white point by name. Argument is one of the entries returned from list_white_points, or a temperature value like "6800K". This changes the current color slightly since white-point adaptation is not completely reversible. This does not affect the current RGB space, thus it is possible to use RGB spaces at whitepoints other than those they were defined at.
570             =cut
571              
572             sub set_white_point
573             {
574 0     0 1 0 my ($this, $white_point) = @_;
575              
576 0         0 $white_point = &_check_white_point($white_point);
577              
578 0 0       0 if (&_check_white_point($this->{white_point}) ne $white_point)
579             {
580 0         0 $this->{xyz} = &XYZ_change_white_point($this->{xyz}, $this->get_XYZ_white(), $this->get_XYZ_white($white_point));
581 0         0 $this->{white_point} = $white_point;
582             }
583              
584 0         0 return $this;
585             }
586              
587             =head2 $rgb_space = $color->get_rgb_space()
588             Returns the name of the current RGB color space. Value is one of the entries returned from list_rgb_spaces, such as "NTSC".
589             =cut
590              
591             # FIXME should be always defined
592             sub get_rgb_space
593             {
594 0     0 1 0 my ($this) = @_;
595 0         0 return $this->{space};
596             }
597              
598             =head2 $color->set_rgb_space("NTSC")
599             Sets the current RGB color space by name. Argument is one of the entries returned from list_rgb_spaces. This may change the current color if the old and new spaces have different white points.
600             =cut
601              
602             sub set_rgb_space
603             {
604 0     0 1 0 my ($this, $space) = @_;
605 0         0 my $s = &_get_RGB_space_by_name($space);
606 0 0       0 if ($this->get_white_point() ne $s->{white_point})
607             {
608 0         0 $this->set_white_point($s->{white_point});
609             }
610 0         0 $this->{space} = $space;
611 0         0 return $this;
612             }
613              
614             =head2 $color2 = $color->copy()
615             Creates an exact duplicate of the current color.
616             =cut
617              
618             sub copy
619             {
620 0     0 1 0 my ($this) = @_;
621 0         0 my $copy = +{
622             xyz => $this->{xyz},
623             space => $this->{space},
624             white_point => $this->{white_point}
625             };
626 0         0 bless $copy, ref $this;
627 0         0 return $copy;
628             }
629              
630              
631             =head2 if ($color->equals($color2)) { ... }
632             Checks if another color is the same as this one. Optionally takes an accuracy parameter which is the distance between the two colors as measured by the city-block metric in XYZ space (default accuracy is 0.01%).
633             =cut
634              
635             sub equals
636             {
637 0     0 1 0 my ($this, $other, %opts) = @_;
638 0         0 $other = $other->copy();
639 0         0 $other->set_white_point($this->{white_point});
640 0         0 $other->set_rgb_space($this->{space});
641 0   0     0 my $accuracy = $opts{accuracy} || 0.0001;
642 0 0       0 if (&_delta_v3($this->{xyz}, $other->{xyz}) < $accuracy) { return 1; }
  0         0  
643 0         0 else { return 0; }
644             }
645              
646             =head2 $d = $color->difference($color2)
647             Calculates the difference between this color and another one. The difference measure is (approximately) perceptually uniform.
648             =cut
649              
650             sub difference
651             {
652 0     0 1 0 my ($this, $other) = @_;
653 0         0 return $this->difference_CIE1976($other);
654             }
655              
656             # reference: http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE76.html
657             sub difference_CIE1976
658             {
659 0     0 0 0 my ($this, $other) = @_;
660            
661 0         0 my ($L1, $a1, $b1) = @{ $this->as_Lab() };
  0         0  
662 0         0 my ($L2, $a2, $b2) = @{ $other->as_Lab() };
  0         0  
663            
664 0         0 my $deltaE = sqrt(&_sqr($L1-$L2) + &_sqr($a1-$a2) + &_sqr($b1-$b2));
665            
666 0         0 return $deltaE;
667             }
668              
669             sub difference_CIE1994
670 0     0 0 0 {
671             # TODO
672             }
673              
674             # reference: http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html
675             sub difference_CMC
676             {
677 0     0 0 0 my ($this, $other, %opts) = @_;
678              
679 0   0     0 my $l = $opts{l} || 1;
680 0   0     0 my $c = $opts{c} || 1;
681            
682 0         0 my ($L1, $a1, $b1) = @{ $this->as_Lab() };
  0         0  
683 0         0 my ($L2, $a2, $b2) = @{ $other->as_Lab() };
  0         0  
684              
685 0         0 my $C1 = sqrt($a1*$a1 + $b1*$b1);
686 0         0 my $C2 = sqrt($a2*$a2 + $b2*$b2);
687              
688 0         0 my $dH = sqrt(&_sqr($a1-$a2) + &_sqr($b1-$b2) - &_sqr($C1-$C2));
689              
690 0 0       0 my $SL = ($L1 < 16 ?
691             0.511 :
692             0.040975 * $L1 / ( 1 + 0.01765 * $L1)
693             );
694              
695 0         0 my $SC = 0.638 + 0.0638 * $C1 / ( 1 + 0.0131 * $C1 );
696              
697 0         0 my $F = sqrt(pow($C1, 4) / ( pow($C1, 4) + 1900 ));
698              
699 0         0 my $H1 = atan2($b1, $a1);
700              
701 0 0 0     0 my $T = ((deg2rad(164) <= $H1 && $H1 <= deg2rad(345)) ?
702             0.56 + abs(0.2 * cos($H1 + deg2rad(168))) :
703             0.36 + abs(0.4 * cos($H1 + deg2rad(35)))
704             );
705              
706 0         0 my $SH = $SC * ($F*$T - $F + 1);
707            
708 0         0 my $deltaE = sqrt(&_sqr(($L1 - $L2)/($l * $SL)) +
709             &_sqr(($C1 - $C2)/($c * $SC)) +
710             &_sqr($dH/$SH)
711             );
712              
713 0         0 return $deltaE;
714             }
715              
716             =head2 @colorspaces = &Graphics::ColorObject->list_colorspaces()
717             Returns a list of all supported colorspaces.
718             =cut
719              
720             sub list_colorspaces
721             {
722 16     16 1 6969 return qw(RGB XYZ xyY Lab LCHab Luv LCHuv HSL HSV CMY CMYK YCbCr YPbPr YUV YIQ); # PhotoYCC
723             }
724              
725             =head2 @rgb_spaces = &Graphics::ColorObject->list_rgb_spaces()
726             Returns a list of all supported RGB spaces. Some items are aliases, so the same space may be listed more than once under different names.
727             =cut
728              
729             sub list_rgb_spaces
730             {
731 26     26 1 16200 return sort keys %RGB_SPACES;
732             }
733              
734             =head2 @white_points = &Graphics::ColorObject->list_white_points()
735             Returns a list of all supported white points.
736             =cut
737              
738             sub list_white_points
739             {
740 0     0 1 0 return sort keys %WHITE_POINTS;
741             }
742              
743              
744             ############# non-OO interface ###########
745              
746             sub RGB_to_RGB255
747             {
748 1     1 0 2 my ($rgb) = @_;
749 1         2 my ($r, $g, $b) = @{$rgb};
  1         3  
750 1 50       6 if ($r < 0) { $r = 0; } elsif ($r > 1) { $r = 1; }
  0 50       0  
  0         0  
751 1 50       5 if ($g < 0) { $g = 0; } elsif ($g > 1) { $g = 1; }
  0 50       0  
  0         0  
752 1 50       5 if ($b < 0) { $b = 0; } elsif ($b > 1) { $b = 1; }
  0 50       0  
  0         0  
753             # FIXME use round, not sprintf
754 1         12 return [ sprintf('%.0f', 255*$r), sprintf('%.0f', 255*$g), sprintf('%.0f', 255*$b) ];
755             }
756              
757             sub RGB255_to_RGB
758             {
759 3     3 0 4 my ($rgb255) = @_;
760 3         5 my ($r, $g, $b) = @{$rgb255};
  3         6  
761 3         21 return [ $r/255, $g/255, $b/255 ];
762             }
763              
764             sub RGBhex_to_RGB
765             {
766 3     3 0 5 my ($rgbhex) = @_;
767 3         4 my ($r, $g, $b);
768 3 50       19 if ($rgbhex =~ m!^\#([0-9a-fA-F]{6})!) { $rgbhex = $1; }
  3         10  
769 3 50       14 if ($rgbhex =~ m!^[0-9a-fA-F]{6}$!)
770             {
771 3         7 $r=hex(substr($rgbhex,0,2));
772 3         8 $g=hex(substr($rgbhex,2,2));
773 3         5 $b=hex(substr($rgbhex,4,2));
774             }
775 3         13 return &RGB255_to_RGB([$r, $g, $b]);
776             # return &RGB255_to_RGB([ unpack("C*",pack("N",hex($rgbhex)<<8)) ]);
777             }
778              
779             sub RGB_to_RGBhex
780             {
781 1     1 0 2 my ($rgb) = @_;
782 1         5 my $rgb255 = &RGB_to_RGB255($rgb);
783 1         2 return sprintf('%02X%02X%02X', @{$rgb255});
  1         8  
784             }
785              
786             sub RGB_to_XYZ
787             {
788 45125     45125 0 73524 my ($rgb, $space) = @_;
789 45125         100815 my $s = &_get_RGB_space_by_name($space);
790 45125         117924 my $rgb_lin = &RGB_to_linear_RGB($rgb, $space);
791 45125         119053 my $xyz = &_mult_v3_m33($rgb_lin, $s->{m});
792 45125         188174 return ($xyz);
793             }
794              
795             sub XYZ_to_RGB
796             {
797 45122     45122 0 67151 my ($xyz, $space) = @_;
798 45122         88408 my $s = &_get_RGB_space_by_name($space);
799 45122         146665 my $rgb_lin = &_mult_v3_m33($xyz, $s->{mstar});
800 45122         134145 my $rgb = &linear_RGB_to_RGB($rgb_lin, $space);
801 45122         142268 return ($rgb);
802             }
803              
804             sub XYZ_to_Lab
805             {
806 2162     2162 0 3293 my ($xyz, $xyz_white) = @_;
807 2162         2182 my ($X, $Y, $Z) = @{$xyz};
  2162         3729  
808 2162         2481 my ($Xw, $Yw, $Zw) = @{$xyz_white};
  2162         3784  
809 2162         2273 my ($L, $a, $b);
810              
811 2162         2370 my $epsilon = 0.008856;
812 2162         2602 my $kappa = 903.3;
813              
814 2162         2022 my ($fx, $fy, $fz);
815 2162         4210 my ($xr, $yr, $zr) = ( $X / $Xw,
816             $Y / $Yw,
817             $Z / $Zw );
818              
819 2162 100       4402 if ($xr > $epsilon) { $fx = pow($xr, 1/3); } else { $fx = ($kappa*$xr + 16)/116; }
  1982         48914  
  180         427  
820 2162 100       15083 if ($yr > $epsilon) { $fy = pow($yr, 1/3); } else { $fy = ($kappa*$yr + 16)/116; }
  1982         45807  
  180         287  
821 2162 100       12670 if ($zr > $epsilon) { $fz = pow($zr, 1/3); } else { $fz = ($kappa*$zr + 16)/116; }
  1802         43267  
  360         676  
822              
823 2162         11413 $L = 116 * $fy - 16;
824 2162         2855 $a = 500 * ($fx - $fy);
825 2162         2750 $b = 200 * ($fy - $fz);
826              
827 2162         7982 return [ $L, $a, $b ];
828             }
829              
830             sub Lab_to_XYZ
831             {
832 2161     2161 0 3216 my ($lab, $xyz_white) = @_;
833 2161         2313 my ($L, $a, $b) = @{$lab};
  2161         3915  
834 2161         3095 my ($Xw, $Yw, $Zw) = @{$xyz_white};
  2161         3634  
835 2161         2263 my ($X, $Y, $Z);
836              
837 2161         2476 my $epsilon = 0.008856;
838 2161         2536 my $kappa = 903.3;
839              
840 2161         2236 my ($fx, $fy, $fz);
841 0         0 my ($xr, $yr, $zr);
842              
843 2161 100       5691 if ($L > $kappa*$epsilon) { $yr = pow( ($L + 16)/116, 3 ); } else { $yr = $L / $kappa; }
  1981         58550  
  180         247  
844 2161 100       16592 if ( $yr > $epsilon ) { $fy = ($L + 16)/116; } else { $fy = ($kappa*$yr + 16)/116; }
  1981         3486  
  180         348  
845              
846 2161         3006 $fx = ($a / 500) + $fy;
847 2161         3355 $fz = $fy - ($b / 200);
848              
849 2161 100       51595 if (pow($fx, 3) > $epsilon) { $xr = pow($fx, 3); } else { $xr = (116 * $fx - 16)/$kappa; }
  1981         61335  
  180         1545  
850 2161 100       59829 if (pow($fz, 3) > $epsilon) { $zr = pow($fz, 3); } else { $zr = (116 * $fz - 16)/$kappa; }
  1801         49626  
  360         2640  
851 2161 100       16474 if ($L > $kappa*$epsilon) { $yr = pow(($L + 16)/116, 3); } else { $yr = $L/$kappa; }
  1981         51626  
  180         237  
852              
853 2161         10633 $X = $xr * $Xw;
854 2161         2635 $Y = $yr * $Yw;
855 2161         2531 $Z = $zr * $Zw;
856            
857 2161         9117 return [ $X, $Y, $Z ];
858             }
859              
860              
861             sub RGB_to_Lab
862             {
863 0     0 0 0 my ($rgb, $space) = @_;
864 0         0 my $xyz_white = &RGB_to_XYZ([ 1.0, 1.0, 1.0 ], $space);
865 0         0 my $xyz = &RGB_to_XYZ($rgb, $space);
866              
867 0         0 return &XYZ_to_Lab($xyz, $xyz_white);
868             }
869              
870             sub Lab_to_RGB
871             {
872 0     0 0 0 my ($lab, $space) = @_;
873 0         0 my $xyz_white = &RGB_to_XYZ([ 1.0, 1.0, 1.0 ], $space);
874 0         0 my $xyz = &Lab_to_XYZ($lab, $xyz_white);
875              
876 0         0 return &XYZ_to_RGB($xyz, $space);
877             }
878              
879             sub XYZ_to_Luv
880             {
881 2160     2160 0 3597 my ($xyz, $xyz_white) = @_;
882 2160         2264 my ($X, $Y, $Z) = @{$xyz};
  2160         3764  
883 2160         2594 my ($Xw, $Yw, $Zw) = @{$xyz_white};
  2160         12798  
884 2160         2491 my ($L, $u, $v);
885              
886 2160         2855 my $epsilon = 0.008856;
887 2160         2609 my $kappa = 903.3;
888              
889 2160         3145 my ($yr) = ( $Y / $Yw );
890              
891 2160 100       3890 if ($yr > $epsilon) { $L = 116 * pow($yr, 1/3) - 16; }
  1980         53542  
892 180         386 else { $L = $kappa*$yr; }
893              
894 2160         13493 my ($up, $vp);
895 0         0 my ($upw, $vpw);
896              
897 2160         7338 ($upw, $vpw) = ( 4 * $Xw / ( $Xw + 15 * $Yw + 3 * $Zw ),
898             9 * $Yw / ( $Xw + 15 * $Yw + 3 * $Zw ) );
899              
900 2160 100 66     8764 if (! ($X == 0 && $Y == 0 && $Z == 0))
901             {
902 1980         6206 ($up, $vp) = ( 4 * $X / ( $X + 15 * $Y + 3 * $Z ),
903             9 * $Y / ( $X + 15 * $Y + 3 * $Z ) );
904             }
905             else
906             {
907 180         294 ($up, $vp) = ($upw, $vpw);
908             }
909              
910 2160         5594 ($u, $v) = ( 13 * $L * ($up - $upw),
911             13 * $L * ($vp - $vpw) );
912              
913 2160         8403 return [ $L, $u, $v ];
914             }
915              
916             sub Luv_to_XYZ
917             {
918 2160     2160 0 12318 my ($luv, $xyz_white) = @_;
919 2160         2333 my ($L, $u, $v) = @{$luv};
  2160         4312  
920 2160         2554 my ($Xw, $Yw, $Zw) = @{$xyz_white};
  2160         3611  
921 2160         2275 my ($X, $Y, $Z);
922              
923 2160         2564 my $epsilon = 0.008856;
924 2160         2244 my $kappa = 903.3;
925              
926 2160 100       4615 if ($L > $kappa*$epsilon) { $Y = pow( ($L + 16)/116, 3 ); } else { $Y = $L / $kappa; }
  1980         56127  
  180         288  
927              
928 2160         18609 my ($upw, $vpw) = ( 4 * $Xw / ( $Xw + 15 * $Yw + 3 * $Zw ),
929             9 * $Yw / ( $Xw + 15 * $Yw + 3 * $Zw ) );
930              
931 2160 100 66     22734 if (! ($L == 0 && $u == 0 && $v == 0))
932             {
933 1980         4391 my $a = (1/3)*( ((52 * $L) / ($u + 13 * $L * $upw)) - 1 );
934 1980         2625 my $b = -5 * $Y;
935 1980         2079 my $c = -1/3;
936 1980         3463 my $d = $Y * ( ((39 * $L) / ($v + 13 * $L * $vpw)) - 5 );
937            
938 1980         2847 $X = ($d - $b)/($a - $c);
939 1980         2764 $Z = $X * $a + $b;
940             }
941             else
942             {
943 180         259 ($X, $Z) = (0.0, 0.0);
944             }
945              
946 2160         8548 return [ $X, $Y, $Z ];
947             }
948              
949             sub Luv_to_LCHuv
950             {
951 1080     1080 0 1765 my ($luv) = @_;
952 1080         1098 my ($L, $u, $v) = @{$luv};
  1080         1827  
953 1080         1264 my ($C, $H);
954              
955 1080         1865 $C = sqrt( $u*$u + $v*$v );
956 1080         2583 $H = atan2( $v, $u );
957 1080         3313 $H = rad2deg($H);
958              
959 1080         12873 return [ $L, $C, $H ];
960             }
961              
962             sub LCHuv_to_Luv
963             {
964 1080     1080 0 1427 my ($lch) = @_;
965 1080         1191 my ($L, $C, $H) = @{$lch};
  1080         2195  
966 1080         1415 my ($u, $v);
967              
968 1080         4863 $H = deg2rad($H);
969 1080         13129 my $th = tan($H);
970 1080         17312 $u = $C / sqrt( $th * $th + 1 );
971 1080         1676 $v = sqrt($C*$C - $u*$u);
972              
973             #$H = $H - 2*pi*int($H / 2*pi); # convert H to 0..2*pi - this seems to be wrong
974 1080 100       2621 if ($H < 0) { $H = $H + 2*pi; }
  405         770  
975 1080 100 100     4366 if ($H > pi/2 && $H < 3*pi/2) { $u = - $u; }
  585         892  
976 1080 100       2327 if ($H > pi) { $v = - $v; }
  405         640  
977              
978 1080         7238 return [ $L, $u, $v ];
979             }
980              
981             sub XYZ_to_xyY
982             {
983 1080     1080 0 1723 my ($xyz, $xyz_white) = @_;
984 1080         1606 my ($X, $Y, $Z) = @{$xyz};
  1080         1815  
985 1080         1316 my ($Xw, $Yw, $Zw) = @{$xyz_white};
  1080         1775  
986 1080         1260 my ($x, $y);
987              
988 1080 100 66     3795 if (! ($X == 0 && $Y == 0 && $Z == 0))
989             {
990 990         1456 $x = $X / ($X + $Y + $Z);
991 990         1468 $y = $Y / ($X + $Y + $Z);
992             }
993             else
994             {
995 90         165 $x = $Xw / ( $Xw + $Yw + $Zw );
996 90         178 $y = $Yw / ( $Xw + $Yw + $Zw );
997             }
998            
999 1080         3109 return [ $x, $y, $Y ];
1000             }
1001              
1002             sub xyY_to_XYZ
1003             {
1004 10803     10803 0 15054 my ($xyy) = @_;
1005 10803         13780 my ($x, $y, $Y) = @{$xyy};
  10803         17956  
1006 10803         13049 my ($X, $Z);
1007              
1008 10803 50       43468 if (! ($y == 0))
1009             {
1010 10803         16053 $X = $x * $Y / $y;
1011 10803         17142 $Z = (1 - $x - $y) * $Y / $y;
1012             }
1013             else
1014             {
1015 0         0 $X = 0; $Y = 0; $Z = 0;
  0         0  
  0         0  
1016             }
1017              
1018 10803         47463 return [ $X, $Y, $Z ];
1019             }
1020              
1021              
1022             sub Lab_to_LCHab
1023             {
1024 1081     1081 0 1359 my ($lab) = @_;
1025 1081         1086 my ($L, $a, $b) = @{$lab};
  1081         2224  
1026 1081         6314 my ($C, $H);
1027              
1028 1081         1774 $C = sqrt( $a*$a + $b*$b );
1029 1081         25923 $H = atan2( $b, $a );
1030 1081         3216 $H = rad2deg($H);
1031              
1032 1081         15082 return [ $L, $C, $H ];
1033             }
1034              
1035              
1036             sub LCHab_to_Lab
1037             {
1038 1080     1080 0 1560 my ($lch) = @_;
1039 1080         1197 my ($L, $C, $H) = @{$lch};
  1080         2085  
1040 1080         1239 my ($a, $b);
1041              
1042 1080         3087 $H = deg2rad($H);
1043 1080         13144 my $th = tan($H);
1044 1080         16516 $a = $C / sqrt( $th * $th + 1 );
1045 1080         1609 $b = sqrt($C*$C - $a*$a);
1046              
1047             #$H = $H - 2*pi*int($H / 2*pi); # convert H to 0..2*pi - this seems to be wrong
1048 1080 100       2412 if ($H < 0) { $H = $H + 2*pi; }
  405         701  
1049 1080 100 100     4645 if ($H > pi/2 && $H < 3*pi/2) { $a = - $a; }
  585         1099  
1050 1080 100       2333 if ($H > pi) { $b = - $b; }
  405         552  
1051              
1052 1080         4891 return [ $L, $a, $b ];
1053             }
1054              
1055             sub RGB_to_linear_RGB
1056             {
1057 45125     45125 0 80697 my ($rgb, $space) = @_;
1058 45125         68039 my ($R, $G, $B) = @{$rgb};
  45125         92445  
1059              
1060 45125         87175 my $s = &_get_RGB_space_by_name($space);
1061 45125 100       318270 if ($s->{gamma} eq 'sRGB') # handle special sRGB gamma curve
1062             {
1063 3600 100       8564 if ( abs($R) <= 0.04045 ) { $R = $R / 12.92; }
  840         1333  
1064 2760         7453 else { $R = &_apow( ( $R + 0.055 ) / 1.055 , 2.4 ); }
1065              
1066 3600 100       26342 if ( abs($G) <= 0.04045 ) { $G = $G / 12.92; }
  915         1547  
1067 2685         7761 else { $G = &_apow( ( $G + 0.055 ) / 1.055 , 2.4 ); }
1068              
1069 3600 100       20874 if ( abs($B) <= 0.04045 ) { $B = $B / 12.92; }
  885         1316  
1070 2715         6666 else { $B = &_apow( ( $B + 0.055 ) / 1.055 , 2.4 ); }
1071             }
1072             else
1073             {
1074 41525         99192 $R = &_apow($R, $s->{gamma});
1075 41525         373781 $G = &_apow($G, $s->{gamma});
1076 41525         315770 $B = &_apow($B, $s->{gamma});
1077             }
1078              
1079 45125         386844 return [ $R, $G, $B ];
1080             }
1081              
1082             sub linear_RGB_to_RGB
1083             {
1084 45122     45122 0 70634 my ($rgb, $space) = @_;
1085 45122         52984 my ($R, $G, $B) = @{$rgb};
  45122         79883  
1086              
1087 45122         96681 my $s = &_get_RGB_space_by_name($space);
1088 45122 100       238268 if ($s->{gamma} eq 'sRGB') # handle special sRGB gamma curve
1089             {
1090 3600 100       8214 if ( abs($R) <= 0.0031308 ) { $R = 12.92 * $R; }
  840         1236  
1091 2760         6334 else { $R = 1.055 * &_apow($R, 1/2.4) - 0.055; };
1092              
1093 3600 100       24565 if ( abs($G) <= 0.0031308 ) { $G = 12.92 * $G; }
  915         1160  
1094 2685         5247 else { $G = 1.055 * &_apow($G, 1/2.4) - 0.055; }
1095              
1096 3600 100       24566 if ( abs($B) <= 0.0031308 ) { $B = 12.92 * $B; }
  885         5803  
1097 2715         5334 else { $B = 1.055 * &_apow($B, 1/2.4) - 0.055; }
1098             }
1099             else
1100             {
1101 41522         125036 $R = &_apow($R, 1/$s->{gamma});
1102 41522         335001 $G = &_apow($G, 1/$s->{gamma});
1103 41522         298690 $B = &_apow($B, 1/$s->{gamma});
1104             }
1105              
1106 45122         371482 return [ $R, $G, $B ];
1107             }
1108              
1109             # reference: http://en.wikipedia.org/wiki/YIQ
1110             sub RGB_to_YIQ
1111             {
1112 1080     1080 0 1567 my ($rgb) = @_; # input should be CIE Rec 601/NTSC non-linear rgb
1113 1080         5022 my $m = [[0.299 , 0.587 , 0.114 ],
1114             [0.59590059, -0.27455667, -0.32134392],
1115             [0.21153661, -0.52273617, 0.31119955]];
1116              
1117 1080         3286 my $yiq = &_mult_m33_v3($m, $rgb);
1118 1080         3168 return $yiq;
1119             }
1120              
1121             sub YIQ_to_RGB
1122             {
1123 1080     1080 0 1395 my ($yiq) = @_;
1124 1080         4263 my $mstar = [[ 1.0 , 0.95598634, 0.6208248 ],
1125             [ 1.0 , -0.27201283, -0.64720424],
1126             [ 1.0 , -1.1067402 , 1.7042305 ]];
1127              
1128 1080         2420 my $rgb = &_mult_m33_v3($mstar, $yiq);
1129 1080         4387 return $rgb; # result is NTSC non-linear rgb
1130             }
1131              
1132             sub RGB_to_YUV
1133             {
1134 1080     1080 0 1448 my ($rgb) = @_; # input should be CIE Rec 601/NTSC non-linear rgb
1135 1080         5383 my $m = [[ 0.299 , 0.587 , 0.114 ],
1136             [-0.14714119, -0.28886916, 0.43601035 ],
1137             [ 0.61497538, -0.51496512, -0.10001026 ]];
1138              
1139 1080         2475 my $yuv = &_mult_m33_v3($m, $rgb);
1140 1080         5113 return $yuv;
1141             }
1142              
1143             sub YUV_to_RGB
1144             {
1145 1080     1080 0 1837 my ($yuv) = @_;
1146 1080         5240 my $mstar = [[ 1.0, 0.0 , 1.139883 ],
1147             [ 1.0, -0.39464233, -0.58062185],
1148             [ 1.0, 2.0320619 , 0.0 ]];
1149              
1150 1080         2870 my $rgb = &_mult_m33_v3($mstar, $yuv);
1151 1080         10774 return $rgb; # result is NTSC non-linear rgb
1152             }
1153              
1154             # reference: http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.txt
1155             # Y is [0..1], Pb and Pr are [-0.5..0.5]
1156             sub RGB_to_YPbPr
1157             {
1158 1080     1080 0 2290 my ($rgb) = @_; # input should be CIE Rec 601/NTSC non-linear rgb
1159 1080         4923 my $m = [[ 0.299 , 0.587 , 0.114 ],
1160             [-0.168736,-0.331264, 0.5 ],
1161             [ 0.5 ,-0.418688,-0.081312]];
1162              
1163 1080         2623 my $ypbpr = &_mult_m33_v3($m, $rgb);
1164 1080         8885 return $ypbpr;
1165             }
1166              
1167             sub YPbPr_to_RGB
1168             {
1169 1080     1080 0 2854 my ($ypbpr) = @_;
1170 1080         4415 my $mstar = [[ 1.0 , 0.0 , 1.402 ],
1171             [ 1.0 ,-0.344136,-0.714136],
1172             [ 1.0 , 1.772 , 0.0 ]];
1173              
1174 1080         3911 my $rgb = &_mult_m33_v3($mstar, $ypbpr);
1175 1080         12691 return $rgb; # result is NTSC non-linear rgb
1176             }
1177              
1178             # Y is [16..235], Cb and Cr are [16..239]
1179             sub RGB_to_YCbCr
1180             {
1181 1080     1080 0 1412 my ($rgb) = @_; # input should be NTSC non-linear rgb
1182 1080         5128 my $m = [[ 65.481, 128.553, 24.966],
1183             [ -37.797, -74.203, 112.0 ],
1184             [ 112.0 , -93.786, -18.214]];
1185              
1186 1080         2676 my $ycbcr = &_add_v3( &_mult_m33_v3($m, $rgb), [ 16, 128, 128 ] );
1187 1080         3982 return $ycbcr;
1188             }
1189              
1190             sub YCbCr_to_RGB
1191             {
1192 1080     1080 0 1291 my ($ycbcr) = @_;
1193 1080         4041 my $mstar = [[ 0.00456621, 0.0 , 0.00625893],
1194             [ 0.00456621,-0.00153632,-0.00318811],
1195             [ 0.00456621, 0.00791071, 0.0 ]];
1196              
1197 1080         3441 my $rgb = &_mult_m33_v3($mstar, &_add_v3($ycbcr, [-16, -128, -128]));
1198 1080         5284 return $rgb;
1199             }
1200              
1201             # reference: http://wwwde.kodak.com/global/en/professional/products/storage/pcd/techInfo/pcd-045.jhtml
1202             sub RGB_to_PhotoYCC
1203             {
1204 0     0 0 0 my ($rgb) = @_; # input should be CIE Rec 709 non-linear rgb
1205 0         0 my $m = [[ 0.299 , 0.587 , 0.114 ],
1206             [-0.299 , -0.587 , 0.866 ],
1207             [ 0.701 , -0.587 , -0.114 ]];
1208 0         0 my $ycc =
1209             &_add_v3([0, 156, 137],
1210             &_mult_m33_v3([[255/1.402, 0, 0], [0, 111.40, 0], [0, 0, 135.64]],
1211             &_mult_m33_v3($m, $rgb)));
1212 0         0 return $ycc;
1213             }
1214              
1215             sub PhotoYCC_to_RGB
1216             {
1217 0     0 0 0 my ($ycc) = @_;
1218 0         0 my $mstar = [[ 1.0 , 0.0 , 1.0 ],
1219             [ 0.99603657, -0.19817126, -0.50936968],
1220             [ 1.0204082 , 1.0204082 , 0.0 ]];
1221              
1222 0         0 my $rgb = &_mult_m33_v3($mstar,
1223             &_mult_m33_v3([[1/(255/1.402), 0, 0], [0, 1/111.40, 0], [0, 0, 1/135.64]],
1224             &_add_v3([0, -156, -137], $ycc)));
1225 0         0 return $rgb; # result is CIE 709 non-linear rgb
1226             }
1227              
1228             sub RGB_to_HSV
1229             {
1230 2160     2160 0 2624 my ($rgb) = @_;
1231 2160         2467 my ($r, $g, $b)=@{$rgb};
  2160         3612  
1232 2160         2695 my ($h, $s, $v);
1233              
1234 2160         3853 my $min= &_min($r, $g, $b);
1235 2160         4539 my $max= &_max($r, $g, $b);
1236              
1237 2160         2952 $v = $max;
1238 2160         2766 my $delta = $max - $min;
1239              
1240 2160 100       3867 if( $delta != 0 )
1241             {
1242 1922         2560 $s = $delta / $max;
1243             }
1244             else
1245             {
1246 238         337 $s = 0;
1247 238         265 $h = 0;
1248 238         967 return [ $h, $s, $v];
1249             }
1250              
1251 1922 100       4238 if( $r == $max )
    100          
1252             {
1253 1010         1290 $h = ( $g - $b ) / $delta;
1254             }
1255             elsif ( $g == $max )
1256             {
1257 500         837 $h = 2 + ( $b - $r ) / $delta;
1258             }
1259             else # if $b == $max
1260             {
1261 412         690 $h = 4 + ( $r - $g ) / $delta;
1262             }
1263              
1264 1922         2637 $h *= 60;
1265 1922 100       4044 if( $h < 0 ) { $h += 360; }
  484         633  
1266 1922         5881 return [ $h, $s, $v ];
1267             }
1268              
1269             sub HSV_to_RGB
1270             {
1271 1080     1080 0 1345 my ($hsv) = @_;
1272 1080         1515 my ($h, $s, $v)=@{$hsv};
  1080         2220  
1273 1080         1491 my ($r, $g, $b);
1274              
1275             # force $h to 0 <= $h < 360
1276             # FIXME should not loop, looks infinite
1277 1080         2621 while ($h < 0) { $h += 360; }
  0         0  
1278 1080         2143 while ($h >= 360) { $h -= 360; }
  0         0  
1279              
1280 1080         1405 $h /= 60; ## sector 0 to 5
1281 1080         3265 my $i = POSIX::floor( $h );
1282 1080         1863 my $f = $h - $i; ## fractional part of h
1283 1080         1609 my $p = $v * ( 1 - $s );
1284 1080         1566 my $q = $v * ( 1 - $s * $f );
1285 1080         1589 my $t = $v * ( 1 - $s * ( 1 - $f ) );
1286              
1287 1080 100       3798 if($i == 0)
    100          
    100          
    100          
    100          
1288             {
1289 346         545 $r = $v;
1290 346         441 $g = $t;
1291 346         476 $b = $p;
1292             }
1293             elsif($i == 1)
1294             {
1295 174         241 $r = $q;
1296 174         241 $g = $v;
1297 174         214 $b = $p;
1298             }
1299             elsif($i == 2)
1300             {
1301 42         60 $r = $p;
1302 42         50 $g = $v;
1303 42         50 $b = $t;
1304             }
1305             elsif($i == 3)
1306             {
1307 178         246 $r = $p;
1308 178         244 $g = $q;
1309 178         208 $b = $v;
1310             }
1311             elsif($i == 4)
1312             {
1313 90         144 $r = $t;
1314 90         116 $g = $p;
1315 90         121 $b = $v;
1316             }
1317             else # if $i == 5
1318             {
1319 250         318 $r = $v;
1320 250         381 $g = $p;
1321 250         283 $b = $q;
1322             }
1323              
1324 1080         4914 return [ $r, $g, $b ];
1325             }
1326              
1327             sub RGB_to_HSL
1328             {
1329 1080     1080 0 1443 my ($rgb) = @_;
1330 1080         1174 my ($r,$g,$b)=@{$rgb};
  1080         2016  
1331              
1332 1080         1261 my ($h, $s, $v) = @{ &RGB_to_HSV($rgb) };
  1080         2046  
1333              
1334 1080         2909 my $min= &_min($r, $g, $b);
1335 1080         1939 my $max= &_max($r, $g, $b);
1336 1080         1535 my $delta = $max - $min;
1337              
1338 1080         1658 my $l = ($max+$min)/2.0;
1339              
1340 1080 100       3836 if( $delta == 0 )
1341             {
1342 119         348 return [0, 0, $l];
1343             }
1344             else
1345             {
1346 961 100       1816 if($l <= 0.5)
1347             {
1348 692         1100 $s = $delta/($max+$min); # FIXME possible divide-by-zero
1349             }
1350             else
1351             {
1352 269         482 $s = $delta/(2-$max-$min); # FIXME possible divide-by-zero
1353             }
1354             }
1355 961         2931 return [$h, $s, $l];
1356             }
1357              
1358             sub HSL_to_RGB
1359             {
1360 1080     1080 0 1460 my ($hsl) = @_;
1361 1080         1285 my ($h, $s, $l) = @{$hsl};
  1080         2065  
1362 1080         1490 my ($r, $g, $b);
1363 0         0 my ($p1, $p2);
1364              
1365 1080 100       2204 if( $l <= 0.5 )
1366             {
1367 811         1311 $p1 = $l * (1-$s);
1368 811         1145 $p2 = 2*$l - $p1;
1369             }
1370             else
1371             {
1372 269         576 $p2 = $l + $s - ($l*$s);
1373 269         436 $p1 = 2*$l - $p2;
1374             }
1375            
1376 1080         2596 $r = &_rgbquant($p1, $p2, $h+120);
1377 1080         2110 $g = &_rgbquant($p1, $p2, $h);
1378 1080         2100 $b = &_rgbquant($p1, $p2, $h-120);
1379            
1380 1080         4614 return [ $r, $g, $b ];
1381             }
1382              
1383             sub _rgbquant {
1384 3240     3240   4701 my ($q1, $q2, $h) = @_;
1385              
1386             # force $h to 0 <= $h < 360
1387             # FIXME should not loop, looks infinite
1388 3240         7107 while ($h < 0) { $h += 360; }
  520         1156  
1389 3240         6050 while ($h >= 360) { $h -= 360; }
  363         707  
1390              
1391 3240 100       7963 if ($h < 60)
    100          
    100          
1392             {
1393 501         1487 return ($q1 + (($q2-$q1)*$h/60) );
1394             }
1395             elsif ($h < 180)
1396             {
1397 1044         1898 return ($q2);
1398             }
1399             elsif ($h < 240)
1400             {
1401 638         1808 return ($q1 + (($q2-$q1)*(240-$h)/60) );
1402             }
1403             else
1404             {
1405 1057         1849 return ($q1);
1406             }
1407             }
1408              
1409             sub RGB_to_CMY
1410             {
1411 2160     2160 0 3368 my ($rgb) = @_;
1412 2160         2547 return [ map { 1 - $_ } @{$rgb} ];
  6480         15460  
  2160         3709  
1413             }
1414              
1415             sub CMY_to_RGB
1416             {
1417 2160     2160 0 2980 my ($cmy) = @_;
1418 2160         2447 return [ map { 1 - $_ } @{$cmy} ];
  6480         23872  
  2160         3820  
1419             }
1420              
1421             sub CMY_to_CMYK
1422             {
1423 1080     1080 0 1437 my ($cmy) = @_;
1424 1080         1115 my $k = &_min(@{$cmy});
  1080         2487  
1425 1080         1520 return [ (map { $_-$k } @{$cmy}),$k ];
  3240         6954  
  1080         1648  
1426             }
1427              
1428             sub CMYK_to_CMY
1429             {
1430 1080     1080 0 1320 my ($cmyk) = @_;
1431 1080         1185 my ($c, $m, $y, $k) = @{$cmyk};
  1080         1945  
1432 1080         4293 return [ $c+$k, $m+$k, $y+$k ];
1433             }
1434              
1435             sub XYZ_change_white_point
1436             {
1437 0     0 0 0 my ($xyz, $xyz_old_white_point, $xyz_new_white_point) = @_;
1438              
1439             # matrices for Bradford color-adaptation
1440 0         0 my $ma = [[ 0.8951, -0.7502, 0.0389 ],
1441             [ 0.2664, 1.7135, -0.0685 ],
1442             [ -0.1614, 0.0367, 1.0296 ]];
1443              
1444 0         0 my $ma_star = [[ 0.986993, 0.432305, -0.008529 ],
1445             [-0.147054, 0.518360, 0.040043 ],
1446             [ 0.159963, 0.049291, 0.968487 ]];
1447              
1448             # cone = cone response domain value (rho, ypsilon, beta)
1449 0         0 my $cone_old = &_mult_v3_m33($xyz_old_white_point, $ma);
1450 0         0 my $cone_new = &_mult_v3_m33($xyz_new_white_point, $ma);
1451              
1452 0         0 my $q = [[ $cone_new->[0]/$cone_old->[0], 0, 0 ],
1453             [ 0, $cone_new->[1]/$cone_old->[1], 0 ],
1454             [ 0, 0, $cone_new->[2]/$cone_old->[2] ]];
1455            
1456 0         0 my $m = &_mult_m33_m33($ma, &_mult_m33_m33($q, $ma_star));
1457              
1458 0         0 my $xyz_new = &_mult_v3_m33($xyz, $m);
1459              
1460 0         0 return $xyz_new;
1461             }
1462              
1463             # reference: http://www.brucelindbloom.com/index.html?Eqn_T_to_xy.html
1464             sub white_point_from_temperature
1465             {
1466 0     0 0 0 my ($temp) = @_;
1467 0         0 my ($x, $y);
1468              
1469 0 0 0     0 if ($temp < 4000 || $temp > 25000)
1470             {
1471 0         0 carp "color temperature out of range: $temp, should be between 4000 and 25000 Kelvin";
1472             }
1473              
1474 0 0       0 if ($temp <= 7000)
1475             {
1476 0         0 $x = -4.6070e+9 / ($temp*$temp*$temp) +
1477             2.9678e+6 / ($temp*$temp) +
1478             0.09911e+3 / $temp +
1479             0.244063;
1480             }
1481             else # $temp > 7000
1482             {
1483 0         0 $x = -2.0064e+9 / ($temp*$temp*$temp) +
1484             1.9018e+6 / ($temp*$temp) +
1485             0.24748e+3 / $temp +
1486             0.237040;
1487             }
1488              
1489 0         0 $y = -3.0 * $x * $x + 2.87 * $x - 0.275;
1490              
1491 0         0 return [ $x, $y ];
1492             }
1493              
1494              
1495             ######### private utility functions ########
1496              
1497             sub _get_RGB_space_by_name
1498             {
1499 190214     190214   307675 my ($space) = @_;
1500             # FIXME the logic here is a bit convoluted, this could be cleaned up a lot
1501              
1502 190214 50       658041 if (! defined $space)
    50          
1503             {
1504             # carp("no rgb space specified in operation that requires it, defaulting to sRGB");
1505 0         0 $space = 'sRGB';
1506             }
1507             elsif (! $RGB_SPACES{ $space })
1508             {
1509 0         0 carp("rgb space not found: ".$space.", defaulting to sRGB");
1510 0         0 $space = 'sRGB';
1511             }
1512              
1513 190214         342706 my $s = $RGB_SPACES{$space};
1514 190214 100 66     948658 if ($s && ! ref $s)
1515             {
1516 43200         73163 $s = $RGB_SPACES{$s}; # follow aliases
1517             }
1518              
1519 190214         435584 return $s;
1520             }
1521              
1522             sub _check_white_point
1523             {
1524 9723     9723   18859 my ($white_point) = @_;
1525              
1526 9723 50       51980 if (! defined $white_point)
    50          
    50          
1527             {
1528             # carp("no white point specified in operation that requires it, defaulting to D65");
1529 0         0 $white_point = 'D65';
1530             }
1531             elsif ($white_point =~ m!^(\d+)K$!)
1532             {
1533 0         0 my $temperature = $1;
1534             #$white_point = $temperature.'K'; # already in that form
1535 0         0 $WHITE_POINTS{ $white_point } = &white_point_from_temperature($temperature);
1536             }
1537             elsif (! $WHITE_POINTS{ $white_point })
1538             {
1539 0         0 carp("white point not found: ". $white_point.", defaulting to D65");
1540 0         0 $white_point = 'D65';
1541             }
1542              
1543 9723         18011 return $white_point;
1544             }
1545              
1546             sub _mult_v3_m33
1547             {
1548 90247     90247   136265 my ($v, $m) = @_;
1549 90247         615813 my $vout = [
1550             ( $v->[0] * $m->[0]->[0] + $v->[1] * $m->[1]->[0] + $v->[2] * $m->[2]->[0] ),
1551             ( $v->[0] * $m->[0]->[1] + $v->[1] * $m->[1]->[1] + $v->[2] * $m->[2]->[1] ),
1552             ( $v->[0] * $m->[0]->[2] + $v->[1] * $m->[1]->[2] + $v->[2] * $m->[2]->[2] )
1553             ];
1554 90247         175496 return $vout;
1555             }
1556              
1557             sub _mult_m33_v3
1558             {
1559 22240     22240   7986138 my ($m, $v) = @_;
1560 22240         204102 my $vout = [
1561             ( $v->[0] * $m->[0]->[0] + $v->[1] * $m->[0]->[1] + $v->[2] * $m->[0]->[2] ),
1562             ( $v->[0] * $m->[1]->[0] + $v->[1] * $m->[1]->[1] + $v->[2] * $m->[1]->[2] ),
1563             ( $v->[0] * $m->[2]->[0] + $v->[1] * $m->[2]->[1] + $v->[2] * $m->[2]->[2] )
1564             ];
1565 22240         70778 return $vout;
1566             }
1567              
1568             sub _mult_m33_m33
1569             {
1570 0     0   0 my ($m, $n) = @_;
1571 0         0 my $q = [];
1572 0         0 foreach my $i (0..2)
1573             {
1574 0         0 foreach my $j (0..2)
1575             {
1576 0         0 foreach my $k (0..2)
1577             {
1578 0         0 $q->[$i]->[$j] += $m->[$i]->[$k] * $n->[$k]->[$j];
1579             }
1580             }
1581             }
1582 0         0 return $q;
1583             }
1584              
1585             sub _add_v3
1586             {
1587 8960     8960   14860 my ($a, $b) = @_;
1588 8960         40906 my $c = [ $a->[0] + $b->[0],
1589             $a->[1] + $b->[1],
1590             $a->[2] + $b->[2] ];
1591 8960         22474 return $c;
1592             }
1593              
1594             sub _pow_v3
1595             {
1596 0     0   0 my ($v3, $c) = @_;
1597 0         0 my $v3out = [ pow($v3->[0], $c), pow($v3->[1], $c), pow($v3->[2], $c) ];
1598 0         0 return $v3out;
1599             }
1600              
1601             sub _delta_v3
1602             {
1603 20408     20408   190612 my ($a3, $b3) = @_;
1604             return (
1605 20408         104851 abs($a3->[0] - $b3->[0]) +
1606             abs($a3->[1] - $b3->[1]) +
1607             abs($a3->[2] - $b3->[2]) );
1608             }
1609              
1610             sub _generic_clip
1611             {
1612 0     0   0 my ($v3, $c32) = @_;
1613 0 0       0 if ($v3->[0] < $c32->[0]->[0]) { $v3->[0] = $c32->[0]->[0]; }
  0         0  
1614 0 0       0 if ($v3->[0] > $c32->[0]->[1]) { $v3->[0] = $c32->[0]->[1]; }
  0         0  
1615 0 0       0 if ($v3->[1] < $c32->[1]->[0]) { $v3->[1] = $c32->[1]->[0]; }
  0         0  
1616 0 0       0 if ($v3->[1] > $c32->[1]->[1]) { $v3->[1] = $c32->[1]->[1]; }
  0         0  
1617 0 0       0 if ($v3->[2] < $c32->[2]->[0]) { $v3->[2] = $c32->[2]->[0]; }
  0         0  
1618 0 0       0 if ($v3->[2] > $c32->[2]->[1]) { $v3->[2] = $c32->[2]->[1]; }
  0         0  
1619 0         0 return $v3;
1620             }
1621              
1622             sub _apow
1623             {
1624 265461     265461   427936 my ($v, $p) = @_;
1625 265461 100       7630115 return ($v >= 0 ?
1626             pow($v, $p) :
1627             -pow(-$v, $p));
1628             }
1629              
1630             sub _sqr
1631             {
1632 0     0   0 my ($v) = @_;
1633 0         0 return $v*$v;
1634             }
1635              
1636             sub _is_zero
1637             {
1638 0     0   0 my ($v) = @_;
1639 0         0 return (abs($v) < 0.000001);
1640             }
1641              
1642 4320 100   4320   5616 sub _min { my $min = shift(@_); foreach my $v (@_) { if ($v <= $min) { $min = $v; } }; return $min; }
  4320         7397  
  8640         19840  
  4707         25042  
  4320         13354  
1643              
1644 3240 100   3240   3864 sub _max { my $max = shift(@_); foreach my $v (@_) { if ($v >= $max) { $max = $v; } }; return $max; }
  3240         4595  
  6480         14468  
  2899         5316  
  3240         5900  
1645              
1646             ######### colorspace tables ########
1647              
1648             # reference: http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
1649             # All the rgb spaces that this module knows about.
1650             # Key is the name, value is either another name (i.e. this is an alias), or a hashref containg a white point, gamma, a conversion matrix m for rgb-to-xyz and a reverse matrix mstar for xyz-to-rgb transformations
1651             our %RGB_SPACES = (
1652             'Adobe' => 'Adobe RGB (1998)',
1653             'Adobe RGB (1998)' => {
1654             white_point => 'D65',
1655             gamma => 2.2,
1656             m => [ [ 0.5767001212121210, 0.2973609999999999, 0.0270328181818181 ], [ 0.1855557042253521, 0.6273550000000000, 0.0706878873239437 ], [ 0.1882125000000000, 0.0752850000000000, 0.9912525000000000 ] ],
1657             mstar => [ [ 2.0414778828777158, -0.9692568708746859, 0.0134454339800522 ], [ -0.5649765261191881, 1.8759931170154693, -0.1183725462165374 ], [ -0.3447127732462102, 0.0415556248231326, 1.0152620834741313 ] ],
1658             },
1659             'Apple' => 'Apple RGB',
1660             'Apple RGB' => {
1661             white_point => 'D65',
1662             gamma => 1.8,
1663             m => [ [ 0.4496948529411764, 0.2446340000000000, 0.0251829117647059 ], [ 0.3162512941176471, 0.6720340000000000, 0.1411836134453782 ], [ 0.1845208571428572, 0.0833320000000000, 0.9226042857142855 ] ],
1664             mstar => [ [ 2.9517603398020569, -1.0851001264872848, 0.0854802409232915 ], [ -1.2895090072470441, 1.9908397072633022, -0.2694550155056003 ], [ -0.4738802866606785, 0.0372022452865781, 1.0911301341384845 ] ],
1665             },
1666             'BestRGB' => {
1667             white_point => 'D50',
1668             gamma => 2.2,
1669             m => [ [ 0.6326700260082926, 0.2284570000000000, 0.0000000000000000 ], [ 0.2045557161290322, 0.7373519999999999, 0.0095142193548387 ], [ 0.1269951428571429, 0.0341910000000000, 0.8156995714285713 ] ],
1670             mstar => [ [ 1.7552588897490133, -0.5441338472581142, 0.0063467101890703 ], [ -0.4836782739368681, 1.5068795234848715, -0.0175760572028268 ], [ -0.2529998994965047, 0.0215528345168675, 1.2256901641540674 ] ],
1671             },
1672             'Beta RGB' => {
1673             white_point => 'D50',
1674             gamma => 2.2,
1675             m => [ [ 0.6712546349614399, 0.3032730000000001, 0.0000000000000001 ], [ 0.1745833659117997, 0.6637859999999999, 0.0407009558998808 ], [ 0.1183817187500000, 0.0329410000000000, 0.7845011448863635 ] ],
1676             mstar => [ [ 1.6832246105012654, -0.7710229999344457, 0.0400016919321019 ], [ -0.4282356869228009, 1.7065573340451357, -0.0885384492378917 ], [ -0.2360181522709381, 0.0446899574535591, 1.2723768250932299 ] ],
1677             },
1678             'BruceRGB' => {
1679             white_point => 'D65',
1680             gamma => 2.2,
1681             m => [ [ 0.4673842424242424, 0.2409950000000000, 0.0219086363636363 ], [ 0.2944540307692308, 0.6835539999999999, 0.0736135076923076 ], [ 0.1886300000000000, 0.0754520000000000, 0.9934513333333335 ] ],
1682             mstar => [ [ 2.7456543761403882, -0.9692568108426551, 0.0112706581772173 ], [ -1.1358911781912031, 1.8759930008236942, -0.1139588771251973 ], [ -0.4350565642146659, 0.0415556222493375, 1.0131069405965349 ] ],
1683             },
1684             'CIE' => {
1685             white_point => 'E',
1686             gamma => 2.2,
1687             m => [ [ 0.4887167547169811, 0.1762040000000000, 0.0000000000000000 ], [ 0.3106804602510461, 0.8129850000000002, 0.0102048326359833 ], [ 0.2006041111111111, 0.0108110000000000, 0.9898071111111111 ] ],
1688             mstar => [ [ 2.3706802022946527, -0.5138847730830187, 0.0052981111618865 ], [ -0.9000427625776859, 1.4253030498717687, -0.0146947611471193 ], [ -0.4706349622815629, 0.0885813466699250, 1.0093845871252884 ] ],
1689             },
1690             'ColorMatch' => {
1691             white_point => 'D50',
1692             gamma => 1.8,
1693             m => [ [ 0.5093438823529410, 0.2748840000000000, 0.0242544705882353 ], [ 0.3209073388429752, 0.6581320000000002, 0.1087821487603307 ], [ 0.1339700000000000, 0.0669850000000000, 0.6921783333333333 ] ],
1694             mstar => [ [ 2.6422872594587332, -1.1119754096457255, 0.0821692807629542 ], [ -1.2234269646206919, 2.0590166676215107, -0.2807234418494614 ], [ -0.3930142794480749, 0.0159613695164458, 1.4559774449385248 ] ],
1695             },
1696             'DonRGB4' => {
1697             white_point => 'D50',
1698             gamma => 2.2,
1699             m => [ [ 0.6457719999999998, 0.2783499999999999, 0.0037113333333334 ], [ 0.1933510457516340, 0.6879700000000001, 0.0179861437908497 ], [ 0.1250971428571429, 0.0336800000000000, 0.8035085714285716 ] ],
1700             mstar => [ [ 1.7603878846606116, -0.7126289975811030, 0.0078207770365325 ], [ -0.4881191497764036, 1.6527436537605511, -0.0347412748629646 ], [ -0.2536122811541382, 0.0416715470705678, 1.2447804103656714 ] ],
1701             },
1702             'ECI' => {
1703             white_point => 'D50',
1704             gamma => 1.8,
1705             m => [ [ 0.6502045454545454, 0.3202500000000000, -0.0000000000000001 ], [ 0.1780773380281691, 0.6020710000000000, 0.0678389859154930 ], [ 0.1359382500000000, 0.0776790000000000, 0.7573702500000002 ] ],
1706             mstar => [ [ 1.7827609790470664, -0.9593624312689213, 0.0859317810050046 ], [ -0.4969845184555761, 1.9477964513641737, -0.1744675553737970 ], [ -0.2690099687053119, -0.0275807381172883, 1.3228286288043098 ] ],
1707             },
1708             'Ekta Space PS5' => {
1709             white_point => 'D50',
1710             gamma => 2.2,
1711             m => [ [ 0.5938923114754098, 0.2606289999999999, 0.0000000000000000 ], [ 0.2729799428571429, 0.7349460000000001, 0.0419969142857143 ], [ 0.0973500000000000, 0.0044250000000000, 0.7832250000000001 ] ],
1712             mstar => [ [ 2.0043787360968186, -0.7110290170493107, 0.0381257297502959 ], [ -0.7304832564783660, 1.6202136618008882, -0.0868766628736253 ], [ -0.2450047962579189, 0.0792227384931296, 1.2725243569115190 ] ],
1713             },
1714             '601' => 'NTSC',
1715             'CIE Rec 601' => 'NTSC',
1716             'NTSC' => {
1717             white_point => 'C',
1718             gamma => 2.2,
1719             m => [ [ 0.6067337272727271, 0.2988389999999999, -0.0000000000000001 ], [ 0.1735638169014085, 0.5868110000000000, 0.0661195492957747 ], [ 0.2001125000000000, 0.1143500000000000, 1.1149125000000002 ] ],
1720             mstar => [ [ 1.9104909450902432, -0.9843106185066585, 0.0583742441336926 ], [ -0.5325921048972800, 1.9984488315135187, -0.1185174047562849 ], [ -0.2882837998985277, -0.0282979742694222, 0.8986095763610844 ] ],
1721             },
1722             'CIE ITU' => 'PAL/SECAM',
1723             'PAL' => 'PAL/SECAM',
1724             'PAL/SECAM' => {
1725             white_point => 'D65',
1726             gamma => 2.2,
1727             m => [ [ 0.4305861818181819, 0.2220210000000001, 0.0201837272727273 ], [ 0.3415450833333333, 0.7066450000000000, 0.1295515833333333 ], [ 0.1783350000000000, 0.0713340000000000, 0.9392309999999999 ] ],
1728             mstar => [ [ 3.0631308078036081, -0.9692570313532748, 0.0678676345258901 ], [ -1.3932854294802033, 1.8759934276211896, -0.2288214781555966 ], [ -0.4757879688629482, 0.0415556317034429, 1.0691933898259074 ] ],
1729             },
1730             'ProPhoto' => {
1731             white_point => 'D50',
1732             gamma => 1.8,
1733             m => [ [ 0.7976742857142858, 0.2880400000000000, 0.0000000000000000 ], [ 0.1351916830080914, 0.7118740000000000, 0.0000000000000000 ], [ 0.0314760000000000, 0.0000860000000000, 0.8284380000000000 ] ],
1734             mstar => [ [ 1.3459444124134017, -0.5445989438461810, -0.0000000000000000 ], [ -0.2556077203964527, 1.5081675237232912, -0.0000000000000000 ], [ -0.0511118080787822, 0.0205351443915685, 1.2070909349884964 ] ],
1735             },
1736             'SMPTE' => 'SMPTE-C',
1737             'SMPTE-C' => {
1738             white_point => 'D65',
1739             gamma => 2.2,
1740             m => [ [ 0.3935554411764707, 0.2123950000000001, 0.0187407352941176 ], [ 0.3652524201680672, 0.7010489999999999, 0.1119321932773109 ], [ 0.1916597142857142, 0.0865560000000000, 0.9582985714285710 ] ],
1741             mstar => [ [ 3.5056956039694129, -1.0690641158576772, 0.0563116543373650 ], [ -1.7396380462846184, 1.9778095119692913, -0.1969933651732733 ], [ -0.5440105230649496, 0.0351719640259221, 1.0500467308790999 ] ],
1742             },
1743             '709' => 'sRGB',
1744             'CIE Rec 709' => 'sRGB',
1745             'sRGB' => {
1746             white_point => 'D65',
1747             gamma => 'sRGB', # 2.4,
1748             m => [ [ 0.4124237575757575, 0.2126560000000000, 0.0193323636363636 ], [ 0.3575789999999999, 0.7151579999999998, 0.1191930000000000 ], [ 0.1804650000000000, 0.0721860000000000, 0.9504490000000001 ] ],
1749             mstar => [ [ 3.2407109439941704, -0.9692581090654827, 0.0556349466243886 ], [ -1.5372603195869781, 1.8759955135292130, -0.2039948042894247 ], [ -0.4985709144606416, 0.0415556779089489, 1.0570639858633826 ] ],
1750             },
1751             'WideGamut' => {
1752             white_point => 'D50',
1753             gamma => 2.2,
1754             m => [ [ 0.7161035660377360, 0.2581870000000001, 0.0000000000000000 ], [ 0.1009296246973366, 0.7249380000000000, 0.0517812857142858 ], [ 0.1471875000000000, 0.0168750000000000, 0.7734375000000001 ] ],
1755             mstar => [ [ 1.4628087611158722, -0.5217931929785991, 0.0349338148323482 ], [ -0.1840625990709008, 1.4472377239217746, -0.0968919015161355 ], [ -0.2743610287417160, 0.0677227300206644, 1.2883952872306403 ] ],
1756             }
1757             );
1758              
1759             # reference: http://www.aim-dtp.net/aim/technology/cie_xyz/cie_xyz.htm
1760             # reference: Wyszecki, G. and Stiles, W. S. Color Science Concepts and Methods, Wiley (2000). ISBN 0471399183
1761             # based on CIE1931 (2 degree FOV)
1762             our %WHITE_POINTS = (
1763             'A' => [ 0.44757, 0.40745 ], # Tungsten lamp 2856K
1764             'D50' => [ 0.34567, 0.35850 ], # Bright tungsten
1765             'B' => [ 0.34842, 0.35161 ], # CIE Std illuminant B
1766             'D55' => [ 0.33242, 0.34743 ], # Cloudy daylight
1767             'E' => [ 0.333333, 0.333333 ], # Normalized reference source
1768             'D65' => [ 0.312713, 0.329016 ], # Daylight 6504K
1769             'C' => [ 0.310063, 0.316158 ], # North daylight 6774K
1770             'D75' => [ 0.29902, 0.31485 ], # 7500K
1771             'D93' => [ 0.28480, 0.29320 ], # old CRT monitors
1772             'F2' => [ 0.37207, 0.37512 ], # Cool white fluorescent 4200K
1773             'F7' => [ 0.31285, 0.32918 ], # Narrow daylight fluorescent 6500K
1774             'F11' => [ 0.38054, 0.37691 ], # Narrow white fluorescent
1775             );
1776              
1777              
1778             =pod
1779              
1780             =head2 EXPORT
1781              
1782             None by default. The 'all' tag causes the non-object-oriented interface to be exported, and you get all the XXX_to_YYY functions, for example RGB_to_XYZ. Please note that some of these functions need extra arguments in addition to the color value to be converted.
1783              
1784             =head1 BUGS
1785              
1786             Backwards compatibility with versions before 0.4 is not very well tested.
1787              
1788             This module will produce results that are, in some cases, different from other software. Most of the time that is not a bug in this module, but rather a case where the other software uses an approximate (trading accuracy for speed) algorithm. That is particularly true for YUV and related conversions which are often implemented using integer-math approximations. As far as possible, this module produces results which are exact according to the definitions in the relevant CIE/ITU or other standards.
1789              
1790             Some color transformations are not exactly reversible. In particular, conversions between different white points are almost but not exactly reversible. This is not a bug.
1791              
1792             There is no way to choose any other color-adaptation algorithm than the Bradford algorithm. That is probably ok since the Bradford algorithm is better than other algorithms (such as Von Kries or simple scaling).
1793              
1794             There is no way to choose a RGB space other than the built-in ones.
1795              
1796             Support for CMYK is very basic, it relies on assumptions that completely do not work in the physical world of subtractive pigment mixtures. If you tried to convert an image to CMYK directly for printing using these functions, the results will not be very good, to say the least.
1797              
1798              
1799             =head1 TODO
1800              
1801             Add clipping to gamut for every color space.
1802              
1803             Choose between several clipping algorithms (nearest, luminance-preserving, hue-preserving).
1804              
1805             Add a simpler way to check whether something is within gamut.
1806              
1807             Add user-defined RGB spaces.
1808              
1809             Calculate RGB matrices from chromaticity coordinates.
1810              
1811             Only load non-RGB matrices once at startup.
1812              
1813             Add colorspaces: uvw, YOZ, RYB, others?
1814              
1815             Add RGB spaces: ROMM, others?
1816              
1817             Convert arrays of colors efficiently (maybe someday in C).
1818              
1819              
1820             =head1 SEE ALSO
1821              
1822             The Color FAQ by Charles Poynton is one of the definitive references on the subject:
1823             http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.txt
1824              
1825             Bruce Lindbloom's web site contains a tremendous amount of information on color:
1826             http://www.brucelindbloom.com/index.html?Math.html
1827              
1828              
1829             =head1 AUTHOR
1830              
1831             Alex Izvorski, Eizv@dslextreme.comE
1832              
1833             Alfred Reibenschuh Ealfredreibenschuh@yahoo.comE was the original author for versions up to 0.3a2.
1834              
1835             Many thanks to:
1836              
1837             Alfred Reibenschuh Ealfredreibenschuh@yahoo.comE for the previous versions of Graphics::ColorObject, and for the HSL/HSV/CMYK code.
1838              
1839             Bruce Lindbloom Einfo@brucelindbloom.comE for providing a wealth of information on color space conversion and color adaptation algorithms, and for the precalculated RGB conversion matrices.
1840              
1841             Charles Poynton Ecolorfaq@poynton.comE for the Color FAQ.
1842              
1843             Timo Autiokari Etimo.autiokari@aim-dtp.netE for information on white points.
1844              
1845              
1846             =head1 COPYRIGHT AND LICENSE
1847              
1848             Copyright 2003-2005 by Alex Izvorski
1849              
1850             Portions Copyright 2001-2003 by Alfred Reibenschuh
1851              
1852             This library is free software; you can redistribute it and/or modify
1853             it under the same terms as Perl itself.
1854              
1855             =cut
1856              
1857             ################ emulation of previous versions (pre-0.4) #################
1858              
1859             #sub mMin {}
1860             #sub mMax {}
1861 0     0 0 0 sub RGBtoHSV { my (@c) = @_; return @{&RGB_to_HSV([@c])}; }
  0         0  
  0         0  
1862 0     0 0 0 sub HSVtoRGB { my (@c) = @_; return @{&HSV_to_RGB([@c])}; }
  0         0  
  0         0  
1863 0     0 0 0 sub RGBtoHSL { my (@c) = @_; return @{&RGB_to_HSL([@c])}; }
  0         0  
  0         0  
1864 0     0 0 0 sub RGBquant { my (@c) = @_; return &_rgbquant(@c); }
  0         0  
1865 0     0 0 0 sub HSLtoRGB { my (@c) = @_; return @{&HSL_to_RGB([@c])}; }
  0         0  
  0         0  
1866             #sub namecolor {} # see below
1867             #sub new {} # if given args that are not a hash, this calls namecolor
1868 0     0 0 0 sub newRGB { my ($p, @c) = @_; return &new_RGB($p, [@c], space=>'NTSC'); }
  0         0  
1869 0     0 0 0 sub newHSV { my ($p, @c) = @_; return &new_HSV($p, [@c], space=>'NTSC'); }
  0         0  
1870 0     0 0 0 sub newHSL { my ($p, @c) = @_; return &new_HSL($p, [@c], space=>'NTSC'); }
  0         0  
1871 0     0 0 0 sub newGrey { my ($p, @c) = @_; return &new_YPbPr($p, [$c[0], 0.0, 0.0], space=>'NTSC'); }
  0         0  
1872 0     0 0 0 sub asRGB { my ($this) = @_; return @{$this->as_RGB()}; }
  0         0  
  0         0  
1873 0     0 0 0 sub asHSV { my ($this) = @_; return @{$this->as_HSV()}; }
  0         0  
  0         0  
1874 0     0 0 0 sub asHSL { my ($this) = @_; return @{$this->as_HSL()}; }
  0         0  
  0         0  
1875 0     0 0 0 sub asGrey { my ($this) = @_; return $this->as_YPbPr()->[0]; }
  0         0  
1876 0     0 0 0 sub asGrey2 { my ($this) = @_; return $this->asGrey(); } # slightly different results
  0         0  
1877 0     0 0 0 sub asLum { my ($this) = @_; return $this->as_HSL()->[2]; }
  0         0  
1878 0     0 0 0 sub asCMY { my ($this) = @_; return @{$this->as_CMY()}; }
  0         0  
  0         0  
1879 0     0 0 0 sub asCMYK { my ($this) = @_; return @{$this->as_CMYK()}; }
  0         0  
  0         0  
1880 0     0 0 0 sub asCMYK2 { my ($this) = @_; return @{$this->as_CMYK()}; } # slightly different results
  0         0  
  0         0  
1881 0     0 0 0 sub asCMYK3 { my ($this) = @_; return (map { $_*0.75 } @{$this->as_CMYK()}); }
  0         0  
  0         0  
  0         0  
1882 0     0 0 0 sub asHex { my ($this) = @_; return '#'.$this->as_RGBhex(); }
  0         0  
1883 0     0 0 0 sub asHexCMYK { my ($this) = @_; return sprintf('%%%02X%02X%02X%02X', map {$_*255} $this->asCMYK()); }
  0         0  
  0         0  
1884 0     0 0 0 sub asHexHSV { my ($this) = @_; return sprintf('!%02X%02X%02X', map {$_*255} $this->asHSV()); }
  0         0  
  0         0  
1885 0     0 0 0 sub setRGB { my ($this, @c) = @_; %{$this} = %{&newRGB(ref $this, @c)}; }
  0         0  
  0         0  
  0         0  
1886 0     0 0 0 sub setHSV { my ($this, @c) = @_; %{$this} = %{&newHSV(ref $this, @c)}; }
  0         0  
  0         0  
  0         0  
1887 0     0 0 0 sub setHSL { my ($this, @c) = @_; %{$this} = %{&newHSL(ref $this, @c)}; }
  0         0  
  0         0  
  0         0  
1888 0     0 0 0 sub setGrey { my ($this, @c) = @_; %{$this} = %{&newGrey(ref $this, @c)}; }
  0         0  
  0         0  
  0         0  
1889 0     0 0 0 sub setHex { my ($this, @c) = @_; %{$this} = %{&new(ref $this, @c)}; }
  0         0  
  0         0  
  0         0  
1890 0     0 0 0 sub addSaturation { my ($this, $s2) = @_; my ($h,$s,$v)=$this->asHSV; $this->setHSV($h,$s+$s2,$v); }
  0         0  
  0         0  
1891 0     0 0 0 sub setSaturation { my ($this, $s2) = @_; my ($h,$s,$v)=$this->asHSV; $this->setHSV($h,$s2,$v); }
  0         0  
  0         0  
1892 0     0 0 0 sub rotHue { my ($this, $h2) = @_; my ($h,$s,$v)=$this->asHSV; $h+=$h2; $h%=360; $this->setHSV($h,$s,$v); }
  0         0  
  0         0  
  0         0  
  0         0  
1893 0     0 0 0 sub setHue { my ($this, $h2) = @_; my ($h,$s,$v)=$this->asHSV; $this->setHSV($h2,$s,$v); }
  0         0  
  0         0  
1894 0     0 0 0 sub addBrightness { my ($this, $v2) = @_; my ($h,$s,$v)=$this->asHSV; $this->setHSV($h,$s,$v+$v2); }
  0         0  
  0         0  
1895 0     0 0 0 sub setBrightness { my ($this, $v2) = @_; my ($h,$s,$v)=$this->asHSV; $this->setHSV($h,$s,$v2); }
  0         0  
  0         0  
1896 0     0 0 0 sub addLightness { my ($this, $l2) = @_; my ($h,$s,$l)=$this->asHSL; $this->setHSL($h,$s,$l+$l2); }
  0         0  
  0         0  
1897 0     0 0 0 sub setLightness { my ($this, $l2) = @_; my ($h,$s,$l)=$this->asHSL; $this->setHSL($h,$s,$l2); }
  0         0  
  0         0  
1898              
1899 7     7   8278 use Graphics::ColorNames;
  7         53826  
  7         8520  
1900              
1901             our %COLORNAMES;
1902             tie %COLORNAMES, 'Graphics::ColorNames', qw(HTML Windows Netscape X);
1903              
1904             sub namecolor {
1905 51609     51609 0 125326 my $name=lc(shift @_);
1906 51609         109738 $name=~s/[^\#!%\&a-z0-9]//g;
1907 51609         63563 my $col;
1908 51609         72981 my $opt=shift @_;
1909 51609 50       305801 if($name=~/^#/) {
    50          
    50          
    50          
1910 0         0 my ($r,$g,$b,$h);
1911 0 0       0 if(length($name)<5) { # zb. #fa4, #cf0
    0          
    0          
1912 0         0 $r=hex(substr($name,1,1))/0xf;
1913 0         0 $g=hex(substr($name,2,1))/0xf;
1914 0         0 $b=hex(substr($name,3,1))/0xf;
1915             } elsif(length($name)<8) { # zb. #ffaa44, #ccff00
1916 0         0 $r=hex(substr($name,1,2))/0xff;
1917 0         0 $g=hex(substr($name,3,2))/0xff;
1918 0         0 $b=hex(substr($name,5,2))/0xff;
1919             } elsif(length($name)<11) { # zb. #fffaaa444, #cccfff000
1920 0         0 $r=hex(substr($name,1,3))/0xfff;
1921 0         0 $g=hex(substr($name,4,3))/0xfff;
1922 0         0 $b=hex(substr($name,7,3))/0xfff;
1923             } else { # zb. #ffffaaaa4444, #ccccffff0000
1924 0         0 $r=hex(substr($name,1,4))/0xffff;
1925 0         0 $g=hex(substr($name,5,4))/0xffff;
1926 0         0 $b=hex(substr($name,9,4))/0xffff;
1927             }
1928 0         0 $col=[$r,$g,$b];
1929             } elsif($name=~/^%/) {
1930 0         0 my ($r,$g,$b,$c,$y,$m,$k);
1931 0 0       0 if(length($name)<6) { # zb. %cmyk
    0          
    0          
1932 0         0 $c=hex(substr($name,1,1))/0xf;
1933 0         0 $m=hex(substr($name,2,1))/0xf;
1934 0         0 $y=hex(substr($name,3,1))/0xf;
1935 0         0 $k=hex(substr($name,4,1))/0xf;
1936             } elsif(length($name)<10) { # zb. %ccmmyykk
1937 0         0 $c=hex(substr($name,1,2))/0xff;
1938 0         0 $m=hex(substr($name,3,2))/0xff;
1939 0         0 $y=hex(substr($name,5,2))/0xff;
1940 0         0 $k=hex(substr($name,7,2))/0xff;
1941             } elsif(length($name)<14) { # zb. %cccmmmyyykkk
1942 0         0 $c=hex(substr($name,1,3))/0xfff;
1943 0         0 $m=hex(substr($name,4,3))/0xfff;
1944 0         0 $y=hex(substr($name,7,3))/0xfff;
1945 0         0 $k=hex(substr($name,10,3))/0xfff;
1946             } else { # zb. %ccccmmmmyyyykkkk
1947 0         0 $c=hex(substr($name,1,4))/0xffff;
1948 0         0 $m=hex(substr($name,5,4))/0xffff;
1949 0         0 $y=hex(substr($name,9,4))/0xffff;
1950 0         0 $k=hex(substr($name,13,4))/0xffff;
1951             }
1952 0 0       0 if($opt) {
1953 0         0 $r=1-$c-$k;
1954 0         0 $g=1-$m-$k;
1955 0         0 $b=1-$y-$k;
1956 0         0 $col=[$r,$g,$b];
1957             } else {
1958 0         0 $r=1-$c-$k;
1959 0         0 $g=1-$m-$k;
1960 0         0 $b=1-$y-$k;
1961 0         0 $col=[$r,$g,$b];
1962             }
1963             } elsif($name=~/^!/) {
1964 0         0 my ($r,$g,$b,$h,$s,$v);
1965 0 0       0 if(length($name)<5) {
    0          
    0          
1966 0         0 $h=360*hex(substr($name,1,1))/0xf;
1967 0         0 $s=hex(substr($name,2,1))/0xf;
1968 0         0 $v=hex(substr($name,3,1))/0xf;
1969             } elsif(length($name)<8) {
1970 0         0 $h=360*hex(substr($name,1,2))/0xff;
1971 0         0 $s=hex(substr($name,3,2))/0xff;
1972 0         0 $v=hex(substr($name,5,2))/0xff;
1973             } elsif(length($name)<11) {
1974 0         0 $h=360*hex(substr($name,1,3))/0xfff;
1975 0         0 $s=hex(substr($name,4,3))/0xfff;
1976 0         0 $v=hex(substr($name,7,3))/0xfff;
1977             } else {
1978 0         0 $h=360*hex(substr($name,1,4))/0xffff;
1979 0         0 $s=hex(substr($name,5,4))/0xffff;
1980 0         0 $v=hex(substr($name,9,4))/0xffff;
1981             }
1982 0 0       0 if($opt) {
1983 0         0 ($r,$g,$b)=HSVtoRGB($h,$s,$v);
1984 0         0 $col=[$r,$g,$b];
1985             } else {
1986 0         0 ($r,$g,$b)=HSVtoRGB($h,$s,$v);
1987 0         0 $col=[$r,$g,$b];
1988             }
1989             } elsif($name=~/^&/) {
1990 0         0 my ($r,$g,$b,$h,$s,$l);
1991 0 0       0 if(length($name)<5) {
    0          
    0          
1992 0         0 $h=360*hex(substr($name,1,1))/0xf;
1993 0         0 $s=hex(substr($name,2,1))/0xf;
1994 0         0 $l=hex(substr($name,3,1))/0xf;
1995             } elsif(length($name)<8) {
1996 0         0 $h=360*hex(substr($name,1,2))/0xff;
1997 0         0 $s=hex(substr($name,3,2))/0xff;
1998 0         0 $l=hex(substr($name,5,2))/0xff;
1999             } elsif(length($name)<11) {
2000 0         0 $h=360*hex(substr($name,1,3))/0xfff;
2001 0         0 $s=hex(substr($name,4,3))/0xfff;
2002 0         0 $l=hex(substr($name,7,3))/0xfff;
2003             } else {
2004 0         0 $h=360*hex(substr($name,1,4))/0xffff;
2005 0         0 $s=hex(substr($name,5,4))/0xffff;
2006 0         0 $l=hex(substr($name,9,4))/0xffff;
2007             }
2008 0 0       0 if($opt) {
2009 0         0 ($r,$g,$b)=HSLtoRGB($h,$s,$l);
2010 0         0 $col=[$r,$g,$b];
2011             } else {
2012 0         0 ($r,$g,$b)=HSLtoRGB($h,$s,$l);
2013 0         0 $col=[$r,$g,$b];
2014             }
2015             } else {
2016 51609 50       316082 if ($COLORNAMES{$name})
2017             {
2018 0         0 my ($r, $g, $b) = &Graphics::ColorNames::hex2tuple($COLORNAMES{$name});
2019 0         0 ($r, $g, $b) = map { $_/0xff } ($r, $g, $b);
  0         0  
2020 0         0 $col=[$r,$g,$b];
2021             }
2022             else
2023             {
2024 51609         1607406 return undef;
2025             }
2026             }
2027 0           return $col;
2028             }
2029              
2030             1;
2031              
2032             __END__