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__ |