line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ICC::Support::PCS; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
93091
|
use strict; |
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
54
|
|
4
|
2
|
|
|
2
|
|
9
|
use Carp; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
135
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = 0.75; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# revised 2019-01-07 |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# Copyright © 2004-2020 by William B. Birkett |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# inherit from Shared |
13
|
2
|
|
|
2
|
|
408
|
use parent qw(ICC::Shared); |
|
2
|
|
|
|
|
307
|
|
|
2
|
|
|
|
|
9
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=encoding utf8 |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
list of supported PCS connection spaces |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
0 - 8-bit ICC CIELAB (100, 0, 0 => 255/255, 128/255, 128/255 = 1, 0.50196, 0.50196) |
20
|
|
|
|
|
|
|
0 - 16-bit ICC CIELAB (100, 0, 0 => 65535/65535, 32896/65535, 32896/65535 = 1, 0.50196, 0.50196) |
21
|
|
|
|
|
|
|
1 - 16-bit ICC legacy L*a*b* (100, 0, 0 => 65280/65535, 32768/65535, 32768/65535 = 0.99611, 0.50001, 0.50001) |
22
|
|
|
|
|
|
|
2 - 16-bit EFI/Monaco L*a*b* (100, 0, 0 => 65535/65535, 32768/65535, 32768/65535 = 1, 0.50001, 0.50001) |
23
|
|
|
|
|
|
|
3 - L*a*b* (100, 0, 0 => 100, 0, 0) |
24
|
|
|
|
|
|
|
4 - LxLyLz (100, 0, 0 => 100, 100, 100) |
25
|
|
|
|
|
|
|
5 - unit LxLyLz (100, 0, 0 => 1, 1, 1) |
26
|
|
|
|
|
|
|
6 - xyY (100, 0, 0 => 0.34570, 0.35854, 100) |
27
|
|
|
|
|
|
|
7 - 16-bit ICC XYZ (100, 0, 0 => 0.9642 * 32768/65535, 32768/65535, 0.8249 * 32768/65535 = 0.48211, 0.50001, 0.41246) |
28
|
|
|
|
|
|
|
8 - 32-bit ICC XYZNumber (100, 0, 0 => 0.9642, 1.0, 0.8249) |
29
|
|
|
|
|
|
|
9 - xyz (100, 0, 0 => 1, 1, 1) |
30
|
|
|
|
|
|
|
10 - XYZ (100, 0, 0 => 96.42, 100, 82.49) |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
explanation and application |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
option 0 is for both 8-bit and 16-bit CIELAB encoding. it is listed twice to show the equivalence. |
35
|
|
|
|
|
|
|
option 1 is the 16-bit L*a*b* encoding from the v2 specification. option 1 also applies to mft2 and ncl2 tags within v4 profiles. |
36
|
|
|
|
|
|
|
option 2 is a non-standard L*a*b* encoding used by EFI and Monaco. |
37
|
|
|
|
|
|
|
option 3 is standard L*a*b* encoding, used in measurement files and floating point tags (e.g. D2Bx, B2Dx). |
38
|
|
|
|
|
|
|
option 4 is L* encoding of the xyz channels. |
39
|
|
|
|
|
|
|
option 5 is unit L* encoding of the xyz channels. |
40
|
|
|
|
|
|
|
option 6 is chromaticity plus Y. |
41
|
|
|
|
|
|
|
option 7 is the 16-bit XYZ encoding used by v2 and v4. 8-bit XYZ encoding is undefined by the ICC specification. |
42
|
|
|
|
|
|
|
option 8 is the 32-bit format used by XYZ tags, and the format used to set absolute colorimetry when creating PCS objects. |
43
|
|
|
|
|
|
|
option 9 is X/Xn, Y/Yn, Z/Zn, as defined in ISO 13655. |
44
|
|
|
|
|
|
|
option 10 is standard XYZ encoding, used in measurement files. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# make PCS connection object |
49
|
|
|
|
|
|
|
# structure of the input/output parameter arrays is: (pcs_connection_space, [white_point, [black_point]]) |
50
|
|
|
|
|
|
|
# white point and black point values are optional. default values are D50 for white point and (0, 0, 0) for black point. |
51
|
|
|
|
|
|
|
# white point and black point are encoded as ICC XYZNumbers, which is how they are stored within ICC profiles. |
52
|
|
|
|
|
|
|
# for explanation of tone compression linearity, see 'tone_compression_notes.txt'. |
53
|
|
|
|
|
|
|
# default tone compression linearity = 0 (linear tone compression). |
54
|
|
|
|
|
|
|
# parameters: () |
55
|
|
|
|
|
|
|
# parameters: (ref_to_input_parameter_array, ref_to_output_parameter_array, [tone_compression_linearity]) |
56
|
|
|
|
|
|
|
sub new { |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# get object class |
59
|
1
|
|
|
1
|
0
|
753
|
my $class = shift(); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# create empty PCS object |
62
|
1
|
|
|
|
|
4
|
my $self = [ |
63
|
|
|
|
|
|
|
{}, # object header |
64
|
|
|
|
|
|
|
[], # parameter array |
65
|
|
|
|
|
|
|
[], # tone compression array |
66
|
|
|
|
|
|
|
0 # clipping flag |
67
|
|
|
|
|
|
|
]; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# if 2 or 3 parameters |
70
|
1
|
50
|
33
|
|
|
9
|
if (@_ == 2 || @_ == 3) { |
|
|
50
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# create new object from parameters |
73
|
0
|
|
|
|
|
0
|
_new_pcs($self, @_); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# if any parameters |
76
|
|
|
|
|
|
|
} elsif (@_) { |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# error |
79
|
0
|
|
|
|
|
0
|
croak('wrong number of parameters'); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# bless object |
84
|
1
|
|
|
|
|
3
|
bless($self, $class); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# return object reference |
87
|
1
|
|
|
|
|
2
|
return($self); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# get/set reference to header hash |
92
|
|
|
|
|
|
|
# parameters: ([ref_to_new_hash]) |
93
|
|
|
|
|
|
|
# returns: (ref_to_hash) |
94
|
|
|
|
|
|
|
sub header { |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# get object reference |
97
|
0
|
|
|
0
|
0
|
|
my $self = shift(); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# if there are parameters |
100
|
0
|
0
|
|
|
|
|
if (@_) { |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# if one parameter, a hash reference |
103
|
0
|
0
|
0
|
|
|
|
if (@_ == 1 && ref($_[0]) eq 'HASH') { |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# set header to new hash |
106
|
0
|
|
|
|
|
|
$self->[0] = {%{shift()}}; |
|
0
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
} else { |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# error |
111
|
0
|
|
|
|
|
|
croak('parameter must be a hash reference'); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# return reference |
118
|
0
|
|
|
|
|
|
return($self->[0]); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# get/set clipping flag |
123
|
|
|
|
|
|
|
# if flag is true, values are clipped |
124
|
|
|
|
|
|
|
# parameters: ([new_flag_value]) |
125
|
|
|
|
|
|
|
# returns: (flag_value) |
126
|
|
|
|
|
|
|
sub clip { |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# get object reference |
129
|
0
|
|
|
0
|
1
|
|
my $self = shift(); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# if there are parameters |
132
|
0
|
0
|
|
|
|
|
if (@_) { |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# if one parameter |
135
|
0
|
0
|
|
|
|
|
if (@_ == 1) { |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# set object clipping mask value |
138
|
0
|
|
|
|
|
|
$self->[3] = shift(); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
} else { |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# error |
143
|
0
|
|
|
|
|
|
croak('more than one parameter'); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# return clipping mask value |
150
|
0
|
|
|
|
|
|
return($self->[3]); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# get/set tone compression linearity |
155
|
|
|
|
|
|
|
# parameters: ([new_linearity_value]) |
156
|
|
|
|
|
|
|
# returns: (linearity_value) |
157
|
|
|
|
|
|
|
sub linearity { |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# get object reference |
160
|
0
|
|
|
0
|
0
|
|
my $self = shift(); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# if there are parameters |
163
|
0
|
0
|
|
|
|
|
if (@_) { |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# if one parameter |
166
|
0
|
0
|
|
|
|
|
if (@_ == 1) { |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# set linearity value |
169
|
0
|
|
|
|
|
|
$self->[2][1] = shift(); |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# update tone compression coefficients |
172
|
0
|
|
|
|
|
|
tc_pars($self); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
} else { |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# error |
177
|
0
|
|
|
|
|
|
croak('more than one parameter'); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# return linearity value |
184
|
0
|
|
|
|
|
|
return($self->[2][1]); |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# set input/output gamut scale |
189
|
|
|
|
|
|
|
# sets input/output black point vector to the white point vector x (1 - scale) |
190
|
|
|
|
|
|
|
# a zero value leaves the corresponding black point unchanged |
191
|
|
|
|
|
|
|
# parameters: ([input_gamut_scale_factor, output_gamut_scale_factor]) |
192
|
|
|
|
|
|
|
# returns: (input_gamut_scale_factor, output_gamut_scale_factor) |
193
|
|
|
|
|
|
|
sub scale { |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# get object reference |
196
|
0
|
|
|
0
|
0
|
|
my $self = shift(); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# local variable |
199
|
0
|
|
|
|
|
|
my (@scale); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# if no parameters |
202
|
0
|
0
|
0
|
|
|
|
if (@_ == 0) { |
|
|
0
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# for each scale factor |
205
|
0
|
|
|
|
|
|
for my $i (0 .. 1) { |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# if white point and black point are defined |
208
|
0
|
0
|
0
|
|
|
|
if (defined($self->[1][$i][1]) && defined($self->[1][$i][2]) && $self->[1][$i][1][1] != 0) { |
|
|
|
0
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# set scale value |
211
|
0
|
|
|
|
|
|
$scale[$i] = 1 - ($self->[1][$i][2][1]/$self->[1][$i][1][1]); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
} else { |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# set scale value |
216
|
0
|
|
|
|
|
|
$scale[$i] = 0; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# return |
223
|
0
|
|
|
|
|
|
return(@scale); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# if two numeric parameters |
226
|
0
|
|
|
|
|
|
} elsif (@_ == 2 && 2 == grep {Scalar::Util::looks_like_number($_)} @_) { |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# for each scale factor |
229
|
0
|
|
|
|
|
|
for my $i (0 .. 1) { |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# if gamut scale factor not 0, and white point defined |
232
|
0
|
0
|
0
|
|
|
|
if ($_[$i] && defined($self->[1][$i][1])) { |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# for XYZ |
235
|
0
|
|
|
|
|
|
for my $j (0 .. 2) { |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# set black point to scaled white point value |
238
|
0
|
|
|
|
|
|
$self->[1][$i][2][$j] = (1 - $_[$i]) * $self->[1][$i][1][$j]; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# update tone compression coefficients |
247
|
0
|
|
|
|
|
|
tc_pars($self); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# return |
250
|
0
|
|
|
|
|
|
return(@_); |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
} else { |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# error |
255
|
0
|
|
|
|
|
|
croak('invalid scale inputs'); |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# transform data |
262
|
|
|
|
|
|
|
# supported input types: |
263
|
|
|
|
|
|
|
# parameters: (list, [hash]) |
264
|
|
|
|
|
|
|
# parameters: (vector, [hash]) |
265
|
|
|
|
|
|
|
# parameters: (matrix, [hash]) |
266
|
|
|
|
|
|
|
# parameters: (Math::Matrix_object, [hash]) |
267
|
|
|
|
|
|
|
# parameters: (structure, [hash]) |
268
|
|
|
|
|
|
|
# returns: (same_type_as_input) |
269
|
|
|
|
|
|
|
sub transform { |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# set hash value (0 or 1) |
272
|
0
|
0
|
|
0
|
0
|
|
my $h = ref($_[-1]) eq 'HASH' ? 1 : 0; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# if input a 'Math::Matrix' object |
275
|
0
|
0
|
0
|
|
|
|
if (@_ == $h + 2 && UNIVERSAL::isa($_[1], 'Math::Matrix')) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# call matrix transform |
278
|
0
|
|
|
|
|
|
&_trans2; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# if input an array reference |
281
|
|
|
|
|
|
|
} elsif (@_ == $h + 2 && ref($_[1]) eq 'ARRAY') { |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# if array contains numbers (vector) |
284
|
0
|
0
|
0
|
|
|
|
if (! ref($_[1][0]) && @{$_[1]} == grep {Scalar::Util::looks_like_number($_)} @{$_[1]}) { |
|
0
|
0
|
0
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# call vector transform |
287
|
0
|
|
|
|
|
|
&_trans1; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# if array contains vectors (2-D array) |
290
|
0
|
0
|
|
|
|
|
} elsif (ref($_[1][0]) eq 'ARRAY' && @{$_[1]} == grep {ref($_) eq 'ARRAY' && Scalar::Util::looks_like_number($_->[0])} @{$_[1]}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# call matrix transform |
293
|
0
|
|
|
|
|
|
&_trans2; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
} else { |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# call structure transform |
298
|
0
|
|
|
|
|
|
&_trans3; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# if input a list (of numbers) |
303
|
0
|
|
|
|
|
|
} elsif (@_ == $h + 1 + grep {Scalar::Util::looks_like_number($_)} @_) { |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# call list transform |
306
|
0
|
|
|
|
|
|
&_trans0; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
} else { |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# error |
311
|
0
|
|
|
|
|
|
croak('invalid transform input'); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# invert data |
318
|
|
|
|
|
|
|
# supported input types: |
319
|
|
|
|
|
|
|
# parameters: (list, [hash]) |
320
|
|
|
|
|
|
|
# parameters: (vector, [hash]) |
321
|
|
|
|
|
|
|
# parameters: (matrix, [hash]) |
322
|
|
|
|
|
|
|
# parameters: (Math::Matrix_object, [hash]) |
323
|
|
|
|
|
|
|
# parameters: (structure, [hash]) |
324
|
|
|
|
|
|
|
# returns: (same_type_as_input) |
325
|
|
|
|
|
|
|
sub inverse { |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# set hash value (0 or 1) |
328
|
0
|
0
|
|
0
|
0
|
|
my $h = ref($_[-1]) eq 'HASH' ? 1 : 0; |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# if input a 'Math::Matrix' object |
331
|
0
|
0
|
0
|
|
|
|
if (@_ == $h + 2 && UNIVERSAL::isa($_[1], 'Math::Matrix')) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# call matrix transform |
334
|
0
|
|
|
|
|
|
&_inv2; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# if input an array reference |
337
|
|
|
|
|
|
|
} elsif (@_ == $h + 2 && ref($_[1]) eq 'ARRAY') { |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# if array contains numbers (vector) |
340
|
0
|
0
|
0
|
|
|
|
if (! ref($_[1][0]) && @{$_[1]} == grep {Scalar::Util::looks_like_number($_)} @{$_[1]}) { |
|
0
|
0
|
0
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# call vector transform |
343
|
0
|
|
|
|
|
|
&_inv1; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# if array contains vectors (2-D array) |
346
|
0
|
0
|
|
|
|
|
} elsif (ref($_[1][0]) eq 'ARRAY' && @{$_[1]} == grep {ref($_) eq 'ARRAY' && Scalar::Util::looks_like_number($_->[0])} @{$_[1]}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# call matrix transform |
349
|
0
|
|
|
|
|
|
&_inv2; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
} else { |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# call structure transform |
354
|
0
|
|
|
|
|
|
&_inv3; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# if input a list (of numbers) |
359
|
0
|
|
|
|
|
|
} elsif (@_ == $h + 1 + grep {Scalar::Util::looks_like_number($_)} @_) { |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# call list transform |
362
|
0
|
|
|
|
|
|
&_inv0; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
} else { |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# error |
367
|
0
|
|
|
|
|
|
croak('invalid transform input'); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# compute Jacobian matrix |
374
|
|
|
|
|
|
|
# parameters: (input_vector, [hash]) |
375
|
|
|
|
|
|
|
# returns: (Jacobian_matrix, [output_vector]) |
376
|
|
|
|
|
|
|
sub jacobian { |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# get parameters |
379
|
0
|
|
|
0
|
0
|
|
my ($self, $in, $hash) = @_; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# local variables |
382
|
0
|
|
|
|
|
|
my ($pcsi, $pcso, @t, @d, $jac); |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# verify 3 channels |
385
|
0
|
0
|
|
|
|
|
(@{$in} == 3) or croak('PCS object input not 3 channels'); |
|
0
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# get input PCS |
388
|
0
|
|
|
|
|
|
$pcsi = $self->[1][0][0]; |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# get output PCS |
391
|
0
|
|
|
|
|
|
$pcso = $self->[1][1][0]; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# convert from input PCS |
394
|
0
|
|
|
|
|
|
@t = _rev($pcsi, @{$in}); |
|
0
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# compute _rev Jacobian |
397
|
0
|
|
|
|
|
|
$jac = _rev_jac($pcsi, @{$in}); |
|
0
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# if tone compression is required |
400
|
0
|
0
|
0
|
|
|
|
if ($self->[2][0]) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# if input PCS is L*a*b* |
403
|
0
|
0
|
|
|
|
|
if ($pcsi <= 5) { |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# apply Lab2xyz Jacobian |
406
|
0
|
|
|
|
|
|
$jac = ICC::Shared::Lab2xyz_jac(@t) * $jac; |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# convert to xyz |
409
|
0
|
|
|
|
|
|
@t = ICC::Shared::_Lab2xyz(@t); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
# compute forward derivatives |
414
|
0
|
|
|
|
|
|
@d = _tc_derv($self, 0, @t); |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# for each output |
417
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# for each input |
420
|
0
|
|
|
|
|
|
for my $j (0 .. 2) { |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# adjust Jacobian |
423
|
0
|
|
|
|
|
|
$jac->[$i][$j] *= $d[$i]; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# compute forward tone compression |
430
|
0
|
|
|
|
|
|
@t = _tc($self, 0, @t); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# if output PCS is L*a*b* |
433
|
0
|
0
|
|
|
|
|
if ($pcso <= 5) { |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# apply xyz2Lab Jacobian |
436
|
0
|
|
|
|
|
|
$jac = ICC::Shared::xyz2Lab_jac(@t) * $jac; |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# convert to L*a*b* |
439
|
0
|
|
|
|
|
|
@t = ICC::Shared::_xyz2Lab(@t); |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# if input PCS is L*a*b* and output PCS is xyz |
444
|
|
|
|
|
|
|
} elsif ($pcsi <= 5 && $pcso >= 6) { |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# apply Lab2xyz Jacobian |
447
|
0
|
|
|
|
|
|
$jac = ICC::Shared::Lab2xyz_jac(@t) * $jac; |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# convert to xyz |
450
|
0
|
|
|
|
|
|
@t = ICC::Shared::_Lab2xyz(@t); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# if input PCS is xyz and output PCS is L*a*b* |
453
|
|
|
|
|
|
|
} elsif ($pcsi >= 6 && $pcso <= 5) { |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# apply xyz2Lab Jacobian |
456
|
0
|
|
|
|
|
|
$jac = ICC::Shared::xyz2Lab_jac(@t) * $jac; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# convert to L*a*b* |
459
|
0
|
|
|
|
|
|
@t = ICC::Shared::_xyz2Lab(@t); |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# apply forward Jacobian |
464
|
0
|
|
|
|
|
|
$jac = _fwd_jac($pcso, @t) * $jac; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# if output values wanted |
467
|
0
|
0
|
|
|
|
|
if (wantarray) { |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# return Jacobian and output values |
470
|
0
|
|
|
|
|
|
return($jac, [_fwd($pcso, $self->[3], @t)]); |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
} else { |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# return Jacobian only |
475
|
0
|
|
|
|
|
|
return($jac); |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# compute tone compression coefficients |
482
|
|
|
|
|
|
|
# parameters: (object_reference) |
483
|
|
|
|
|
|
|
sub tc_pars { |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# get object reference |
486
|
0
|
|
|
0
|
0
|
|
my $self = shift(); |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# local variables |
489
|
0
|
|
|
|
|
|
my ($lin, $elin); |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# set tc flag (true if tc required) |
492
|
0
|
0
|
|
|
|
|
$self->[2][0] = grep {$self->[1][0][1][$_] != $self->[1][1][1][$_] || $self->[1][0][2][$_] != $self->[1][1][2][$_]} (0 .. 2); |
|
0
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# if non-linear tone compression |
495
|
0
|
0
|
|
|
|
|
if ($lin = $self->[2][1]) { |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# compute value |
498
|
0
|
|
|
|
|
|
$elin = exp($lin); |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
# for each xyz |
501
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# compute a = (exp(r) - exp(r * y0/y1))/(exp(r) - exp(r * x0/x1)) |
504
|
0
|
|
|
|
|
|
$self->[2][2][$i] = ($elin - exp($lin * $self->[1][1][2][$i]/$self->[1][1][1][$i]))/($elin - exp($lin * $self->[1][0][2][$i]/$self->[1][0][1][$i])); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# compute b = (1 - a) * exp(r) |
507
|
0
|
|
|
|
|
|
$self->[2][3][$i] = (1 - $self->[2][2][$i]) * $elin; |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# else linear tone compression |
512
|
|
|
|
|
|
|
} else { |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# for each xyz |
515
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# compute a = (y1 - y0)/(x1 - x0) |
518
|
0
|
|
|
|
|
|
$self->[2][2][$i] = ($self->[1][1][1][$i] - $self->[1][1][2][$i])/($self->[1][0][1][$i] - $self->[1][0][2][$i]); |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# compute b = y1 - a * x1 |
521
|
0
|
|
|
|
|
|
$self->[2][3][$i] = $self->[1][1][1][$i] - $self->[2][2][$i] * $self->[1][0][1][$i]; |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# print object contents to string |
530
|
|
|
|
|
|
|
# format is an array structure |
531
|
|
|
|
|
|
|
# parameter: ([format]) |
532
|
|
|
|
|
|
|
# returns: (string) |
533
|
|
|
|
|
|
|
sub sdump { |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# get parameters |
536
|
0
|
|
|
0
|
1
|
|
my ($self, $p) = @_; |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# local variables |
539
|
0
|
|
|
|
|
|
my ($s, $fmt); |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
# resolve parameter to an array reference |
542
|
0
|
0
|
|
|
|
|
$p = defined($p) ? ref($p) eq 'ARRAY' ? $p : [$p] : []; |
|
|
0
|
|
|
|
|
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
# get format string |
545
|
0
|
0
|
0
|
|
|
|
$fmt = defined($p->[0]) && ! ref($p->[0]) ? $p->[0] : 'undef'; |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
# set string to object ID |
548
|
0
|
|
|
|
|
|
$s = sprintf("'%s' object, (0x%x)\n", ref($self), $self); |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# return |
551
|
0
|
|
|
|
|
|
return($s); |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# transform list |
556
|
|
|
|
|
|
|
# parameters: (object_reference, list, [hash]) |
557
|
|
|
|
|
|
|
# returns: (list) |
558
|
|
|
|
|
|
|
sub _trans0 { |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# local variables |
561
|
0
|
|
|
0
|
|
|
my ($self, $hash); |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# get object reference |
564
|
0
|
|
|
|
|
|
$self = shift(); |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
# get optional hash |
567
|
0
|
0
|
|
|
|
|
$hash = pop() if (ref($_[-1]) eq 'HASH'); |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# transform single value |
570
|
0
|
|
|
|
|
|
return(_transform($self, 0, @_)); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# transform vector |
575
|
|
|
|
|
|
|
# parameters: (object_reference, vector, [hash]) |
576
|
|
|
|
|
|
|
# returns: (vector) |
577
|
|
|
|
|
|
|
sub _trans1 { |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# get parameters |
580
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# transform vector |
583
|
0
|
|
|
|
|
|
return([_transform($self, 0, @{$in})]); |
|
0
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
# transform matrix (2-D array -or- Math::Matrix object) |
588
|
|
|
|
|
|
|
# parameters: (object_reference, matrix, [hash]) |
589
|
|
|
|
|
|
|
# returns: (matrix) |
590
|
|
|
|
|
|
|
sub _trans2 { |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# get parameters |
593
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# local variable |
596
|
0
|
|
|
|
|
|
my ($out); |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# for each sample |
599
|
0
|
|
|
|
|
|
for my $i (0 .. $#{$in}) { |
|
0
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# transform sample |
602
|
0
|
|
|
|
|
|
$out->[$i] = [_transform($self, 0, @{$in->[$i]})]; |
|
0
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# return |
607
|
0
|
0
|
|
|
|
|
return(UNIVERSAL::isa($in, 'Math::Matrix') ? bless($out, 'Math::Matrix') : $out); |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# transform structure |
612
|
|
|
|
|
|
|
# parameters: (object_reference, structure, [hash]) |
613
|
|
|
|
|
|
|
# returns: (structure) |
614
|
|
|
|
|
|
|
sub _trans3 { |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# get parameters |
617
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
# transform the array structure |
620
|
0
|
|
|
|
|
|
_crawl($self, $in, my $out = [], $hash); |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# return |
623
|
0
|
|
|
|
|
|
return($out); |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
# recursive transform |
628
|
|
|
|
|
|
|
# array structure is traversed until scalar arrays are found and transformed |
629
|
|
|
|
|
|
|
# parameters: (ref_to_object, ref_to_input_array, ref_to_output_array, hash) |
630
|
|
|
|
|
|
|
sub _crawl { |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# get parameters |
633
|
0
|
|
|
0
|
|
|
my ($self, $in, $out, $hash) = @_; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# if input is a vector (reference to a scalar array) |
636
|
0
|
0
|
|
|
|
|
if (@{$in} == grep {! ref()} @{$in}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# transform input vector and copy to output |
639
|
0
|
|
|
|
|
|
@{$out} = @{_trans1($self, $in, $hash)}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
} else { |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
# for each input element |
644
|
0
|
|
|
|
|
|
for my $i (0 .. $#{$in}) { |
|
0
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# if an array reference |
647
|
0
|
0
|
|
|
|
|
if (ref($in->[$i]) eq 'ARRAY') { |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# transform next level |
650
|
0
|
|
|
|
|
|
_crawl($self, $in->[$i], $out->[$i] = [], $hash); |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
} else { |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
# error |
655
|
0
|
|
|
|
|
|
croak('invalid transform input'); |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
# invert list |
666
|
|
|
|
|
|
|
# parameters: (object_reference, list, [hash]) |
667
|
|
|
|
|
|
|
# returns: (list) |
668
|
|
|
|
|
|
|
sub _inv0 { |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
# local variables |
671
|
0
|
|
|
0
|
|
|
my ($self, $hash); |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
# get object reference |
674
|
0
|
|
|
|
|
|
$self = shift(); |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# get optional hash |
677
|
0
|
0
|
|
|
|
|
$hash = pop() if (ref($_[-1]) eq 'HASH'); |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# invert single value |
680
|
0
|
|
|
|
|
|
return(_transform($self, 1, @_)); |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
# invert vector |
685
|
|
|
|
|
|
|
# parameters: (object_reference, vector, [hash]) |
686
|
|
|
|
|
|
|
# returns: (vector) |
687
|
|
|
|
|
|
|
sub _inv1 { |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
# get parameters |
690
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# invert vector |
693
|
0
|
|
|
|
|
|
return([_transform($self, 1, @{$in})]); |
|
0
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# invert matrix (2-D array -or- Math::Matrix object) |
698
|
|
|
|
|
|
|
# parameters: (object_reference, matrix, [hash]) |
699
|
|
|
|
|
|
|
# returns: (matrix) |
700
|
|
|
|
|
|
|
sub _inv2 { |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# get parameters |
703
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# local variable |
706
|
0
|
|
|
|
|
|
my ($out); |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
# for each sample |
709
|
0
|
|
|
|
|
|
for my $i (0 .. $#{$in}) { |
|
0
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
# invert sample |
712
|
0
|
|
|
|
|
|
$out->[$i] = [_transform($self, 1, @{$in->[$i]})]; |
|
0
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# return |
717
|
0
|
0
|
|
|
|
|
return(UNIVERSAL::isa($in, 'Math::Matrix') ? bless($out, 'Math::Matrix') : $out); |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
# invert structure |
722
|
|
|
|
|
|
|
# parameters: (object_reference, structure, [hash]) |
723
|
|
|
|
|
|
|
# returns: (structure) |
724
|
|
|
|
|
|
|
sub _inv3 { |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# get parameters |
727
|
0
|
|
|
0
|
|
|
my ($self, $in, $hash) = @_; |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
# invert the array structure |
730
|
0
|
|
|
|
|
|
_crawl2($self, $in, my $out = [], $hash); |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# return |
733
|
0
|
|
|
|
|
|
return($out); |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# recursive transform |
738
|
|
|
|
|
|
|
# array structure is traversed until scalar arrays are found and inverted |
739
|
|
|
|
|
|
|
# parameters: (ref_to_object, ref_to_input_array, ref_to_output_array, hash) |
740
|
|
|
|
|
|
|
sub _crawl2 { |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# get parameters |
743
|
0
|
|
|
0
|
|
|
my ($self, $in, $out, $hash) = @_; |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
# if input is a vector (reference to a scalar array) |
746
|
0
|
0
|
|
|
|
|
if (@{$in} == grep {! ref()} @{$in}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
# invert input vector and copy to output |
749
|
0
|
|
|
|
|
|
@{$out} = @{_inv1($in, $hash)}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
} else { |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# for each input element |
754
|
0
|
|
|
|
|
|
for my $i (0 .. $#{$in}) { |
|
0
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# if an array reference |
757
|
0
|
0
|
|
|
|
|
if (ref($in->[$i]) eq 'ARRAY') { |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
# invert next level |
760
|
0
|
|
|
|
|
|
_crawl($self, $in->[$i], $out->[$i] = [], $hash); |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
} else { |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# error |
765
|
0
|
|
|
|
|
|
croak('invalid inverse input'); |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
# transform sample data |
776
|
|
|
|
|
|
|
# direction: 0 - normal, 1 - inverse |
777
|
|
|
|
|
|
|
# parameters: (object_reference, direction, array_of_input_values) |
778
|
|
|
|
|
|
|
# returns: (array_of_output_values) |
779
|
|
|
|
|
|
|
sub _transform { |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
# get parameters |
782
|
0
|
|
|
0
|
|
|
my ($self, $dir, @in) = @_; |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# local variables |
785
|
0
|
|
|
|
|
|
my ($i, $pcsi, $pcso, @t); |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
# verify 3 input channels |
788
|
0
|
0
|
|
|
|
|
(@in == 3) or croak('PCS object input not 3 channels'); |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
# get input PCS |
791
|
0
|
|
|
|
|
|
$pcsi = $self->[1][$dir][0]; |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
# get output PCS |
794
|
0
|
|
|
|
|
|
$pcso = $self->[1][1 - $dir][0]; |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
# convert from input PCS |
797
|
0
|
|
|
|
|
|
@t = _rev($pcsi, @in); |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
# if tone compression required |
800
|
0
|
0
|
0
|
|
|
|
if ($self->[2][0]) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
# convert to xyz if input PCS is L*a*b* |
803
|
0
|
0
|
|
|
|
|
@t = ICC::Shared::_Lab2xyz(@t) if ($pcsi <= 5); |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# tone compression |
806
|
0
|
|
|
|
|
|
@t = _tc($self, $dir, @t); |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
# convert to L*a*b* if output PCS is L*a*b* |
809
|
0
|
0
|
|
|
|
|
@t = ICC::Shared::_xyz2Lab(@t) if ($pcso <= 5); |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
# if input PCS is L*a*b* and output PCS is xyz |
812
|
|
|
|
|
|
|
} elsif ($pcsi <= 5 && $pcso >= 6) { |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# convert to xyz |
815
|
0
|
|
|
|
|
|
@t = ICC::Shared::_Lab2xyz(@t); |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
# if input PCS is xyz and output PCS is L*a*b* |
818
|
|
|
|
|
|
|
} elsif ($pcsi >= 6 && $pcso <= 5) { |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
# convert to L*a*b* |
821
|
0
|
|
|
|
|
|
@t = ICC::Shared::_xyz2Lab(@t); |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
} |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
# convert to output PCS and return |
826
|
0
|
|
|
|
|
|
return(_fwd($pcso, $self->[3], @t)); |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
# convert to output PCS |
831
|
|
|
|
|
|
|
# input values are either L*a*b* or xyz, depending on PCS |
832
|
|
|
|
|
|
|
# parameters: (PCS, clipping_flag, array_of_input_values) |
833
|
|
|
|
|
|
|
# returns: (array_of_output_values) |
834
|
|
|
|
|
|
|
sub _fwd { |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
# get parameters |
837
|
0
|
|
|
0
|
|
|
my ($pcs, $clip, @in) = @_; |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# local variable |
840
|
0
|
|
|
|
|
|
my ($denom); |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# if 8-bit ICC CIELAB or 16-bit ICC CIELAB |
843
|
0
|
0
|
|
|
|
|
if ($pcs == 0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
# if clipping flag set |
846
|
0
|
0
|
|
|
|
|
if ($clip) { |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
# return clipped ICC CIELAB values |
849
|
0
|
0
|
|
|
|
|
return(map {$_ < 0 ? 0 : ($_ > 1 ? 1 : $_)} $in[0]/100, ($in[1] + 128)/255, ($in[2] + 128)/255); |
|
0
|
0
|
|
|
|
|
|
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
} else { |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
# return ICC CIELAB values |
854
|
0
|
|
|
|
|
|
return($in[0]/100, ($in[1] + 128)/255, ($in[2] + 128)/255); |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
# if 16-bit ICC legacy L*a*b* |
859
|
|
|
|
|
|
|
} elsif ($pcs == 1) { |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
# if clipping flag set |
862
|
0
|
0
|
|
|
|
|
if ($clip) { |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
# clip L* value |
865
|
0
|
0
|
|
|
|
|
$in[0] = $in[0] > 100 ? 100 : $in[0]; |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
# return clipped 16-bit ICC legacy L*a*b* values |
868
|
0
|
0
|
|
|
|
|
return(map {$_ < 0 ? 0 : ($_ > 1 ? 1 : $_)} $in[0] * 256/25700, ($in[1] + 128) * 256/65535, ($in[2] + 128) * 256/65535); |
|
0
|
0
|
|
|
|
|
|
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
} else { |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
# return 16-bit ICC legacy L*a*b* values |
873
|
0
|
|
|
|
|
|
return($in[0] * 256/25700, ($in[1] + 128) * 256/65535, ($in[2] + 128) * 256/65535); |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
# if 16-bit ICC EFI/Monaco L*a*b* |
878
|
|
|
|
|
|
|
} elsif ($pcs == 2) { |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# if clipping flag set |
881
|
0
|
0
|
|
|
|
|
if ($clip) { |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
# return clipped 16-bit ICC EFI/Monaco L*a*b* values |
884
|
0
|
0
|
|
|
|
|
return(map {$_ < 0 ? 0 : ($_ > 1 ? 1 : $_)} $in[0]/100, ($in[1] + 128) * 256/65535, ($in[2] + 128) * 256/65535); |
|
0
|
0
|
|
|
|
|
|
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
} else { |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
# return 16-bit ICC EFI/Monaco L*a*b* values |
889
|
0
|
|
|
|
|
|
return($in[0]/100, ($in[1] + 128) * 256/65535, ($in[2] + 128) * 256/65535); |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
# if L*a*b* |
894
|
|
|
|
|
|
|
} elsif ($pcs == 3) { |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
# return L*a*b* values |
897
|
0
|
|
|
|
|
|
return(@in); |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# if LxLyLz |
900
|
|
|
|
|
|
|
} elsif ($pcs == 4) { |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
# return LxLyLz values |
903
|
0
|
|
|
|
|
|
return($in[0] + 116 * $in[1]/500, $in[0], $in[0] - 116 * $in[2]/200); |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
# if unit LxLyLz |
906
|
|
|
|
|
|
|
} elsif ($pcs == 5) { |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
# return unit LxLyLz values |
909
|
0
|
|
|
|
|
|
return(map {$_/100} ($in[0] + 116 * $in[1]/500, $in[0], $in[0] - 116 * $in[2]/200)); |
|
0
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
# if xyY |
912
|
|
|
|
|
|
|
} elsif ($pcs == 6) { |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
# compute denominator (X + Y + Z) |
915
|
0
|
|
|
|
|
|
$denom = (96.42 * $in[0] + 100 * $in[1] + 82.49 * $in[2]); |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
# return xyY values |
918
|
0
|
0
|
|
|
|
|
return($denom ? (96.42 * $in[0]/$denom, 100 * $in[1]/$denom, 100 * $in[1]) : (0, 0, 0)); |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
# if 16-bit ICC XYZ |
921
|
|
|
|
|
|
|
} elsif ($pcs == 7) { |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
# if clipping flag set |
924
|
0
|
0
|
|
|
|
|
if ($clip) { |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
# return clipped 16-bit ICC XYZ values |
927
|
0
|
0
|
|
|
|
|
return(map {$_ < 0 ? 0 : ($_ > 1 ? 1 : $_)} $in[0] * 0.482107356374456, $in[1] * 0.500007629510948, $in[2] * 0.412456293583581); |
|
0
|
0
|
|
|
|
|
|
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
} else { |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
# return 16-bit ICC XYZ values |
932
|
0
|
|
|
|
|
|
return($in[0] * 0.482107356374456, $in[1] * 0.500007629510948, $in[2] * 0.412456293583581); |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
# if 32-bit ICC XYZNumber |
937
|
|
|
|
|
|
|
} elsif ($pcs == 8) { |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
# return 32-bit ICC XYZNumber |
940
|
0
|
|
|
|
|
|
return($in[0] * 0.9642, $in[1], $in[2] * 0.8249); |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
# if xyz |
943
|
|
|
|
|
|
|
} elsif ($pcs == 9) { |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
# return xyz values |
946
|
0
|
|
|
|
|
|
return(@in); |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
# if XYZ |
949
|
|
|
|
|
|
|
} elsif ($pcs == 10) { |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
# return XYZ values |
952
|
0
|
|
|
|
|
|
return($in[0] * 96.42, $in[1] * 100, $in[2] * 82.49); |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
} else { |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
# error |
957
|
0
|
|
|
|
|
|
croak('unsupported PCS color space'); |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# convert from input PCS |
964
|
|
|
|
|
|
|
# output values are either L*a*b* or xyz, depending on PCS |
965
|
|
|
|
|
|
|
# parameters: (PCS, array_of_input_values) |
966
|
|
|
|
|
|
|
# returns: (array_of_output_values) |
967
|
|
|
|
|
|
|
sub _rev { |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
# get parameters |
970
|
0
|
|
|
0
|
|
|
my ($pcs, @in) = @_; |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# local variable |
973
|
0
|
|
|
|
|
|
my ($denom); |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
# if 8-bit ICC CIELAB or 16-bit ICC CIELAB |
976
|
0
|
0
|
|
|
|
|
if ($pcs == 0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# return L*a*b* |
979
|
0
|
|
|
|
|
|
return($in[0] * 100, $in[1] * 255 - 128, $in[2] * 255 - 128); |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
# if 16-bit ICC legacy L*a*b* |
982
|
|
|
|
|
|
|
} elsif ($pcs == 1) { |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
# return L*a*b* |
985
|
0
|
|
|
|
|
|
return($in[0] * 25700/256, $in[1] * 65535/256 - 128, $in[2] * 65535/256 - 128); |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
# if 16-bit EFI/Monaco L*a*b* |
988
|
|
|
|
|
|
|
} elsif ($pcs == 2) { |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
# return L*a*b* |
991
|
0
|
|
|
|
|
|
return($in[0] * 100, $in[1] * 65535/256 - 128, $in[2] * 65535/256 - 128); |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
# if L*a*b* |
994
|
|
|
|
|
|
|
} elsif ($pcs == 3) { |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
# return L*a*b* |
997
|
0
|
|
|
|
|
|
return(@in); |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
# if LxLyLz |
1000
|
|
|
|
|
|
|
} elsif ($pcs == 4) { |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
# return L*a*b* |
1003
|
0
|
|
|
|
|
|
return($in[1], 500 * ($in[0] - $in[1])/116, 200 * ($in[1] - $in[2])/116); |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
# if unit LxLyLz |
1006
|
|
|
|
|
|
|
} elsif ($pcs == 5) { |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
# return L*a*b* |
1009
|
0
|
|
|
|
|
|
return(map {$_ * 100} ($in[1], 500 * ($in[0] - $in[1])/116, 200 * ($in[1] - $in[2])/116)); |
|
0
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
# if xyY |
1012
|
|
|
|
|
|
|
} elsif ($pcs == 6) { |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
# compute denominator (X + Y + Z) |
1015
|
0
|
0
|
|
|
|
|
$denom = $in[1] ? $in[2]/$in[1] : 0; |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
# return xyz |
1018
|
0
|
|
|
|
|
|
return($in[0] * $denom/96.42, $in[1] * $denom/100, (1 - $in[0] - $in[1]) * $denom/82.49); |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# if 16-bit ICC XYZ |
1021
|
|
|
|
|
|
|
} elsif ($pcs == 7) { |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
# return xyz |
1024
|
0
|
|
|
|
|
|
return($in[0]/0.482107356374456, $in[1]/0.500007629510948, $in[2]/0.412456293583581); |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
# if ICC XYZNumber |
1027
|
|
|
|
|
|
|
} elsif ($pcs == 8) { |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
# return xyz |
1030
|
0
|
|
|
|
|
|
return($in[0]/0.9642, $in[1], $in[2]/0.8249); |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
# if xyz |
1033
|
|
|
|
|
|
|
} elsif ($pcs == 9) { |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# return xyz |
1036
|
0
|
|
|
|
|
|
return(@in); |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
# if XYZ |
1039
|
|
|
|
|
|
|
} elsif ($pcs == 10) { |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
# return xyz |
1042
|
0
|
|
|
|
|
|
return($in[0]/96.42, $in[1]/100, $in[2]/82.49); |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
} else { |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
# error |
1047
|
0
|
|
|
|
|
|
croak('unsupported PCS color space'); |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
# compute Jacobian matrix for forward transform |
1054
|
|
|
|
|
|
|
# input values are either L*a*b* or xyz, depending on PCS |
1055
|
|
|
|
|
|
|
# parameters: (PCS, array_of_input_values) |
1056
|
|
|
|
|
|
|
# returns: (Jacobian_matrix) |
1057
|
|
|
|
|
|
|
sub _fwd_jac { |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# get parameters |
1060
|
0
|
|
|
0
|
|
|
my ($pcs, @in) = @_; |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
# local variables |
1063
|
0
|
|
|
|
|
|
my ($denom, @out); |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
# if 8-bit ICC CIELAB or 16-bit ICC CIELAB |
1066
|
0
|
0
|
|
|
|
|
if ($pcs == 0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# return Jacobian matrix |
1069
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1070
|
|
|
|
|
|
|
[1/100, 0, 0], |
1071
|
|
|
|
|
|
|
[0, 1/255, 0], |
1072
|
|
|
|
|
|
|
[0, 0, 1/255] |
1073
|
|
|
|
|
|
|
)); |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
# if 16-bit ICC legacy L*a*b* |
1076
|
|
|
|
|
|
|
} elsif ($pcs == 1) { |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
# return Jacobian matrix |
1079
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1080
|
|
|
|
|
|
|
[256/25700, 0, 0], |
1081
|
|
|
|
|
|
|
[0, 256/65535, 0], |
1082
|
|
|
|
|
|
|
[0, 0, 256/65535] |
1083
|
|
|
|
|
|
|
)); |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
# if 16-bit ICC EFI/Monaco L*a*b* |
1086
|
|
|
|
|
|
|
} elsif ($pcs == 2) { |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
# return Jacobian matrix |
1089
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1090
|
|
|
|
|
|
|
[1/100, 0, 0], |
1091
|
|
|
|
|
|
|
[0, 256/65535, 0], |
1092
|
|
|
|
|
|
|
[0, 0, 256/65535] |
1093
|
|
|
|
|
|
|
)); |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
# if L*a*b* |
1096
|
|
|
|
|
|
|
} elsif ($pcs == 3) { |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
# return Jacobian matrix |
1099
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1100
|
|
|
|
|
|
|
[1, 0, 0], |
1101
|
|
|
|
|
|
|
[0, 1, 0], |
1102
|
|
|
|
|
|
|
[0, 0, 1] |
1103
|
|
|
|
|
|
|
)); |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
# if LxLyLz |
1106
|
|
|
|
|
|
|
} elsif ($pcs == 4) { |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
# return Jacobian matrix |
1109
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1110
|
|
|
|
|
|
|
[1, 116/500, 0], |
1111
|
|
|
|
|
|
|
[1, 0, 0], |
1112
|
|
|
|
|
|
|
[1, 0, -116/200] |
1113
|
|
|
|
|
|
|
)); |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
# if unit LxLyLz |
1116
|
|
|
|
|
|
|
} elsif ($pcs == 5) { |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
# return Jacobian matrix |
1119
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1120
|
|
|
|
|
|
|
[1/100, 116/50000, 0], |
1121
|
|
|
|
|
|
|
[1/100, 0, 0], |
1122
|
|
|
|
|
|
|
[1/100, 0, -116/20000] |
1123
|
|
|
|
|
|
|
)); |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
# if xyY |
1126
|
|
|
|
|
|
|
} elsif ($pcs == 6) { |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
# if denominator (X + Y + Z) is non-zero |
1129
|
0
|
0
|
|
|
|
|
if ($denom = (96.42 * $in[0] + 100 * $in[1] + 82.49 * $in[2])) { |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
# compute output vector |
1132
|
0
|
|
|
|
|
|
@out = (96.42 * $in[0]/$denom, 100 * $in[1]/$denom, 100 * $in[1]); |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# return Jacobian matrix |
1135
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1136
|
|
|
|
|
|
|
[96.42 * (1 - $out[0])/$denom, -100 * $out[0]/$denom, -82.49 * $out[0]/$denom], |
1137
|
|
|
|
|
|
|
[-96.42 * $out[1]/$denom, 100 * (1 - $out[1])/$denom, -82.49 * $out[1]/$denom], |
1138
|
|
|
|
|
|
|
[0, 100, 0] |
1139
|
|
|
|
|
|
|
)); |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
} else { |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
# print warning |
1144
|
0
|
|
|
|
|
|
print "Jacobian matrix overflow!\n"; |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
# return Jacobian matrix |
1147
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1148
|
|
|
|
|
|
|
['inf', '-inf', '-inf'], |
1149
|
|
|
|
|
|
|
['-inf', 'inf', '-inf'], |
1150
|
|
|
|
|
|
|
[0, 100, 0] |
1151
|
|
|
|
|
|
|
)); |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
# if 16-bit ICC XYZ |
1156
|
|
|
|
|
|
|
} elsif ($pcs == 7) { |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
# return Jacobian matrix |
1159
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1160
|
|
|
|
|
|
|
[0.482107356374456, 0, 0], |
1161
|
|
|
|
|
|
|
[0, 0.500007629510948, 0], |
1162
|
|
|
|
|
|
|
[0, 0, 0.412456293583581] |
1163
|
|
|
|
|
|
|
)); |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
# if 32-bit ICC XYZNumber |
1166
|
|
|
|
|
|
|
} elsif ($pcs == 8) { |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# return Jacobian matrix |
1169
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1170
|
|
|
|
|
|
|
[0.9642, 0, 0], |
1171
|
|
|
|
|
|
|
[0, 1, 0], |
1172
|
|
|
|
|
|
|
[0, 0, 0.8249] |
1173
|
|
|
|
|
|
|
)); |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
# if xyz |
1176
|
|
|
|
|
|
|
} elsif ($pcs == 9) { |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
# return Jacobian matrix |
1179
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1180
|
|
|
|
|
|
|
[1, 0, 0], |
1181
|
|
|
|
|
|
|
[0, 1, 0], |
1182
|
|
|
|
|
|
|
[0, 0, 1] |
1183
|
|
|
|
|
|
|
)); |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
# if XYZ |
1186
|
|
|
|
|
|
|
} elsif ($pcs == 10) { |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
# return Jacobian matrix |
1189
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1190
|
|
|
|
|
|
|
[96.42, 0, 0], |
1191
|
|
|
|
|
|
|
[0, 100, 0], |
1192
|
|
|
|
|
|
|
[0, 0, 82.49] |
1193
|
|
|
|
|
|
|
)); |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
} else { |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
# error |
1198
|
0
|
|
|
|
|
|
croak('unsupported PCS color space'); |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# compute Jacobian matrix for reverse transform |
1205
|
|
|
|
|
|
|
# output values are either L*a*b* or xyz, depending on PCS |
1206
|
|
|
|
|
|
|
# parameters: (PCS, array_of_input_values) |
1207
|
|
|
|
|
|
|
# returns: (Jacobian_matrix) |
1208
|
|
|
|
|
|
|
sub _rev_jac { |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
# get parameters |
1211
|
0
|
|
|
0
|
|
|
my ($pcs, @in) = @_; |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
# local variables |
1214
|
0
|
|
|
|
|
|
my ($denom, $xr, $zr); |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
# if 8-bit ICC CIELAB or 16-bit ICC CIELAB |
1217
|
0
|
0
|
|
|
|
|
if ($pcs == 0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
# return Jacobian matrix |
1220
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1221
|
|
|
|
|
|
|
[100, 0, 0], |
1222
|
|
|
|
|
|
|
[0, 255, 0], |
1223
|
|
|
|
|
|
|
[0, 0, 255] |
1224
|
|
|
|
|
|
|
)); |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
# if 16-bit ICC legacy L*a*b* |
1227
|
|
|
|
|
|
|
} elsif ($pcs == 1) { |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
# return Jacobian matrix |
1230
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1231
|
|
|
|
|
|
|
[25700/256, 0, 0], |
1232
|
|
|
|
|
|
|
[0, 65535/256, 0], |
1233
|
|
|
|
|
|
|
[0, 0, 65535/256] |
1234
|
|
|
|
|
|
|
)); |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
# if 16-bit EFI/Monaco L*a*b* |
1237
|
|
|
|
|
|
|
} elsif ($pcs == 2) { |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
# return Jacobian matrix |
1240
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1241
|
|
|
|
|
|
|
[100, 0, 0], |
1242
|
|
|
|
|
|
|
[0, 65535/256, 0], |
1243
|
|
|
|
|
|
|
[0, 0, 65535/256] |
1244
|
|
|
|
|
|
|
)); |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
# if L*a*b* |
1247
|
|
|
|
|
|
|
} elsif ($pcs == 3) { |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
# return Jacobian matrix |
1250
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1251
|
|
|
|
|
|
|
[1, 0, 0], |
1252
|
|
|
|
|
|
|
[0, 1, 0], |
1253
|
|
|
|
|
|
|
[0, 0, 1] |
1254
|
|
|
|
|
|
|
)); |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
# if LxLyLz |
1257
|
|
|
|
|
|
|
} elsif ($pcs == 4) { |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
# return Jacobian matrix |
1260
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1261
|
|
|
|
|
|
|
[0, 1, 0], |
1262
|
|
|
|
|
|
|
[500/116, -500/116, 0], |
1263
|
|
|
|
|
|
|
[0, 200/116, -200/116] |
1264
|
|
|
|
|
|
|
)); |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
# if unit LxLyLz |
1267
|
|
|
|
|
|
|
} elsif ($pcs == 5) { |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
# return Jacobian matrix |
1270
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1271
|
|
|
|
|
|
|
[0, 100, 0], |
1272
|
|
|
|
|
|
|
[50000/116, -50000/116, 0], |
1273
|
|
|
|
|
|
|
[0, 20000/116, -20000/116] |
1274
|
|
|
|
|
|
|
)); |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
# if xyY |
1277
|
|
|
|
|
|
|
} elsif ($pcs == 6) { |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
# if y not zero |
1280
|
0
|
0
|
|
|
|
|
if ($in[1]) { |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
# compute denominator (X + Y + Z) |
1283
|
0
|
|
|
|
|
|
$denom = $in[2]/$in[1]; |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
# compute ratios |
1286
|
0
|
|
|
|
|
|
$xr = $in[0]/$in[1]; |
1287
|
0
|
|
|
|
|
|
$zr = (1 - $in[0])/$in[1]; |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# return Jacobian matrix |
1290
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1291
|
|
|
|
|
|
|
[$denom/96.42, -$denom * $xr/96.42, $xr/96.42], |
1292
|
|
|
|
|
|
|
[0, 0, 1/100], |
1293
|
|
|
|
|
|
|
[-$denom/82.49, -$denom * $zr/82.49, ($zr - 1)/82.49] |
1294
|
|
|
|
|
|
|
)); |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
} else { |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
# print warning |
1299
|
0
|
|
|
|
|
|
print "Jacobian matrix overflow!\n"; |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
# return Jacobian matrix |
1302
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1303
|
|
|
|
|
|
|
['inf', '-inf', 'inf'], |
1304
|
|
|
|
|
|
|
[0, 0, 1/100], |
1305
|
|
|
|
|
|
|
['-inf', '-inf', 'inf'] |
1306
|
|
|
|
|
|
|
)); |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
# if 16-bit ICC XYZ |
1311
|
|
|
|
|
|
|
} elsif ($pcs == 7) { |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
# return Jacobian matrix |
1314
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1315
|
|
|
|
|
|
|
[2.074226801931005, 0, 0], |
1316
|
|
|
|
|
|
|
[0, 1.999969482421875, 0], |
1317
|
|
|
|
|
|
|
[0, 0, 2.424499311943114] |
1318
|
|
|
|
|
|
|
)); |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
# if ICC XYZNumber |
1321
|
|
|
|
|
|
|
} elsif ($pcs == 8) { |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
# return Jacobian matrix |
1324
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1325
|
|
|
|
|
|
|
[1/0.9642, 0, 0], |
1326
|
|
|
|
|
|
|
[0, 1, 0], |
1327
|
|
|
|
|
|
|
[0, 0, 1/0.8249] |
1328
|
|
|
|
|
|
|
)); |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
# if xyz |
1331
|
|
|
|
|
|
|
} elsif ($pcs == 9) { |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
# return Jacobian matrix |
1334
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1335
|
|
|
|
|
|
|
[1, 0, 0], |
1336
|
|
|
|
|
|
|
[0, 1, 0], |
1337
|
|
|
|
|
|
|
[0, 0, 1] |
1338
|
|
|
|
|
|
|
)); |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
# if XYZ |
1341
|
|
|
|
|
|
|
} elsif ($pcs == 10) { |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
# return Jacobian matrix |
1344
|
0
|
|
|
|
|
|
return(Math::Matrix->new( |
1345
|
|
|
|
|
|
|
[1/96.42, 0, 0], |
1346
|
|
|
|
|
|
|
[0, 1/100, 0], |
1347
|
|
|
|
|
|
|
[0, 0, 1/82.49] |
1348
|
|
|
|
|
|
|
)); |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
} else { |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
# error |
1353
|
0
|
|
|
|
|
|
croak('unsupported PCS color space'); |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
} |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
} |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
# forward tone compression derivative |
1360
|
|
|
|
|
|
|
# input and output values are xyz |
1361
|
|
|
|
|
|
|
# parameters: (object_reference, direction, array_of_input_values) |
1362
|
|
|
|
|
|
|
# returns: (array_of_output values) |
1363
|
|
|
|
|
|
|
sub _tc_derv { |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
# get parameters |
1366
|
0
|
|
|
0
|
|
|
my ($self, $dir, @in) = @_; |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
# local variables |
1369
|
0
|
|
|
|
|
|
my ($lin, @out, $t, $u); |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
# if non-linear tone compression |
1372
|
0
|
0
|
|
|
|
|
if ($lin = $self->[2][1]) { |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
# if reverse direction |
1375
|
0
|
0
|
|
|
|
|
if ($dir) { |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
# for each xyz |
1378
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
# compute t = (exp(r * y/y1) - b)/a |
1381
|
0
|
|
|
|
|
|
$t = (exp($lin * $in[$i]/$self->[1][1][1][$i]) - $self->[2][3][$i])/$self->[2][2][$i]; |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
# compute u = x1/y1 |
1384
|
0
|
|
|
|
|
|
$u = $self->[1][0][1][$i]/$self->[1][1][1][$i]; |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
# compute x = u * a * exp(r * y/y1)/exp(r * x/x1) |
1387
|
0
|
0
|
|
|
|
|
$out[$i] = $t == 0 ? 1E99 : $u * $self->[2][2][$i] * exp($lin * $in[$i]/$self->[1][1][1][$i])/$t; |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
} |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
} else { |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
# for each xyz |
1394
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
# compute t = exp(r * x/x1) * a + b |
1397
|
0
|
|
|
|
|
|
$t = exp($lin * $in[$i]/$self->[1][0][1][$i]) * $self->[2][2][$i] + $self->[2][3][$i]; |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
# compute u = y1/x1 |
1400
|
0
|
|
|
|
|
|
$u = $self->[1][1][1][$i]/$self->[1][0][1][$i]; |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
# compute x = u * a * (exp(r * x/x1)/exp(r * y/y1)) |
1403
|
0
|
0
|
|
|
|
|
$out[$i] = $t == 0 ? 1E99 : $u * $self->[2][2][$i] * exp($lin * $in[$i]/$self->[1][0][1][$i])/$t; |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
} |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
# if linear tone compression |
1410
|
|
|
|
|
|
|
} else { |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
# if reverse direction |
1413
|
0
|
0
|
|
|
|
|
if ($dir) { |
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
# for each xyz |
1416
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
# compute y = 1/a |
1419
|
0
|
0
|
|
|
|
|
$out[$i] = $self->[2][2][$i] == 0 ? 1E99 : 1/$self->[2][2][$i]; |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
} |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
} else { |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
# for each xyz |
1426
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1427
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
# compute y = a |
1429
|
0
|
|
|
|
|
|
$out[$i] = $self->[2][2][$i]; |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
} |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
} |
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
} |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
# return |
1438
|
0
|
|
|
|
|
|
return(@out); |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
} |
1441
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
# tone compression transform |
1443
|
|
|
|
|
|
|
# input and output values are xyz |
1444
|
|
|
|
|
|
|
# parameters: (object_reference, direction, array_of_input_values) |
1445
|
|
|
|
|
|
|
# returns: (array_of_output values) |
1446
|
|
|
|
|
|
|
sub _tc { |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
# get parameters |
1449
|
0
|
|
|
0
|
|
|
my ($self, $dir, @in) = @_; |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
# local variables |
1452
|
0
|
|
|
|
|
|
my ($lin, @out, $t); |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
# if non-linear tone compression |
1455
|
0
|
0
|
|
|
|
|
if ($lin = $self->[2][1]) { |
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
# if reverse direction |
1458
|
0
|
0
|
|
|
|
|
if ($dir) { |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
# for each xyz |
1461
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
# compute t = (exp(r * y/y1) - b)/a |
1464
|
0
|
|
|
|
|
|
$t = (exp($lin * $in[$i]/$self->[1][1][1][$i]) - $self->[2][3][$i])/$self->[2][2][$i]; |
1465
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
# compute x = ln(t) * x1/r |
1467
|
0
|
0
|
|
|
|
|
$out[$i] = $t > 0 ? log($t) * $self->[1][0][1][$i]/$lin : -1E99; |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
} |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
} else { |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
# for each xyz |
1474
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
# compute t = exp(r * x/x1) * a + b |
1477
|
0
|
|
|
|
|
|
$t = exp($lin * $in[$i]/$self->[1][0][1][$i]) * $self->[2][2][$i] + $self->[2][3][$i]; |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
# compute y = ln(t) * y1/r |
1480
|
0
|
0
|
|
|
|
|
$out[$i] = $t > 0 ? log($t) * $self->[1][1][1][$i]/$lin : -1E99; |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
} |
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
} |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
# else linear tone compression |
1487
|
|
|
|
|
|
|
} else { |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
# if reverse direction |
1490
|
0
|
0
|
|
|
|
|
if ($dir) { |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
# for each xyz |
1493
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
# compute y = (x - b)/a |
1496
|
0
|
0
|
|
|
|
|
$out[$i] = $self->[2][2][$i] == 0 ? 1E99 : ($in[$i] - $self->[2][3][$i])/$self->[2][2][$i]; |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
} |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
} else { |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
# for each xyz |
1503
|
0
|
|
|
|
|
|
for my $i (0 .. 2) { |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
# compute y = ax + b |
1506
|
0
|
|
|
|
|
|
$out[$i] = $in[$i] * $self->[2][2][$i] + $self->[2][3][$i]; |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
} |
1509
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
} |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
} |
1513
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
# return |
1515
|
0
|
|
|
|
|
|
return(@out); |
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
} |
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
# make PCS connection object from parameters |
1520
|
|
|
|
|
|
|
# structure of the input/output parameter arrays is: (pcs_connection_space, [white_point, [black_point]]) |
1521
|
|
|
|
|
|
|
# parameters: (object_reference, ref_to_input_parameter_array, ref_to_output_parameter_array, [tone_compression_linearity]) |
1522
|
|
|
|
|
|
|
sub _new_pcs { |
1523
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
# get object reference |
1525
|
0
|
|
|
0
|
|
|
my ($self) = shift(); |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
# local variables |
1528
|
0
|
|
|
|
|
|
my (@cs, @io); |
1529
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
# list of supported connection spaces |
1531
|
0
|
|
|
|
|
|
@cs = (0 .. 10); |
1532
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
# message labels |
1534
|
0
|
|
|
|
|
|
@io = qw(input output); |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
# for input and output parameters |
1537
|
0
|
|
|
|
|
|
for my $i (0 .. 1) { |
1538
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
# verify parameter is an array reference |
1540
|
0
|
0
|
|
|
|
|
(ref($_[$i]) eq 'ARRAY') or croak("$io[$i] parameter not an array reference"); |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
# verify number of array parameters |
1543
|
0
|
0
|
0
|
|
|
|
(@{$_[$i]} >= 1 || @{$_[$i]} <= 3) or croak("$io[$i] array has wrong number parameters"); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
# verify color space |
1546
|
0
|
0
|
|
|
|
|
(grep {$_[$i][0] == $_} @cs) or croak("$io[$i] color space not supported"); |
|
0
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
# copy color space |
1549
|
0
|
|
|
|
|
|
$self->[1][$i][0] = $_[$i][0]; |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
# if white point is defined |
1552
|
0
|
0
|
|
|
|
|
if (defined($_[$i][1])) { |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
# verify white point is an array reference |
1555
|
0
|
0
|
|
|
|
|
(ref($_[$i][1]) eq 'ARRAY') or croak("$io[$i] white point not an array reference"); |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
# verify array structure |
1558
|
0
|
0
|
|
|
|
|
(3 == grep {! ref()} @{$_[$i][1]}) or croak("$io[$i] white point array has wrong structure"); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
# copy white point (converting XYZNumber to xyz) |
1561
|
0
|
|
|
|
|
|
$self->[1][$i][1] = ICC::Shared::XYZ2xyz($_[$i][1], ICC::Shared::d50); |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
} else { |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
# set white point to perfect white |
1566
|
0
|
|
|
|
|
|
$self->[1][$i][1] = [1, 1, 1]; |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
} |
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
# if black point is defined |
1571
|
0
|
0
|
|
|
|
|
if (defined($_[$i][2])) { |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
# verify black point is an array reference |
1574
|
0
|
0
|
|
|
|
|
(ref($_[$i][2]) eq 'ARRAY') or croak("$io[$i] black point not an array reference"); |
1575
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
# verify array structure |
1577
|
0
|
0
|
|
|
|
|
(3 == grep {! ref()} @{$_[$i][2]}) or croak("$io[$i] black point array has wrong structure"); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
# copy black point (converting XYZNumber to xyz) |
1580
|
0
|
|
|
|
|
|
$self->[1][$i][2] = ICC::Shared::XYZ2xyz($_[$i][2], ICC::Shared::d50); |
1581
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
} else { |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
# set black point to perfect black |
1585
|
0
|
|
|
|
|
|
$self->[1][$i][2] = [0, 0, 0]; |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
} |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
} |
1590
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
# set tone compression linearity (default = 0) |
1592
|
0
|
0
|
|
|
|
|
$self->[2][1] = defined($_[2]) ? $_[2] : 0; |
1593
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
# compute tone compression coefficients |
1595
|
0
|
|
|
|
|
|
tc_pars($self); |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
} |
1598
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
1; |
1600
|
|
|
|
|
|
|
|