File Coverage

lib/ICC/Profile/parf.pm
Criterion Covered Total %
statement 12 118 10.1
branch 0 86 0.0
condition 0 27 0.0
subroutine 4 15 26.6
pod 1 9 11.1
total 17 255 6.6


line stmt bran cond sub pod time code
1             package ICC::Profile::parf;
2              
3 1     1   6 use strict;
  1         1  
  1         72  
4 1     1   7 use Carp;
  1         1  
  1         60  
5              
6             our $VERSION = 0.2;
7              
8             # revised 2018-08-07
9             #
10             # Copyright © 2004-2019 by William B. Birkett
11              
12             # add development directory
13 1     1   5 use lib 'lib';
  1         1  
  1         5  
14              
15             # inherit from Shared
16 1     1   104 use parent qw(ICC::Shared);
  1         2  
  1         4  
17              
18             # parameter count by function type
19             our @Np = (4, 5, 5, 3, 4, 4);
20              
21             # create new parf tag object
22             # parameters: ([ref_to_array])
23             # returns: (ref_to_object)
24             sub new {
25              
26             # get object class
27 0     0 0   my $class = shift();
28              
29             # create empty parf object
30 0           my $self = [
31             {}, # object header
32             [] # parameter array
33             ];
34              
35             # if parameter supplied
36 0 0         if (@_) {
37            
38             # verify array reference
39 0 0         (ref($_[0]) eq 'ARRAY') or croak('not an array reference');
40            
41             # verify function type
42 0 0 0       ($_[0][0] == int($_[0][0]) && defined($Np[$_[0][0]])) or croak('invalid function type');
43            
44             # verify number of parameters
45 0 0         ($#{$_[0]} == $Np[$_[0][0]]) or croak('wrong number of parameters');
  0            
46            
47             # copy array
48 0           $self->[1] = [@{shift()}];
  0            
49            
50             }
51              
52             # bless object
53 0           bless($self, $class);
54              
55             # return object reference
56 0           return($self);
57              
58             }
59              
60             # create parf tag object from ICC profile
61             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
62             # returns: (ref_to_object)
63             sub new_fh {
64              
65             # get object class
66 0     0 0   my $class = shift();
67              
68             # create empty parf object
69 0           my $self = [
70             {}, # object header
71             [] # parameter array
72             ];
73              
74             # verify 3 parameters
75 0 0         (@_ == 3) or croak('wrong number of parameters');
76              
77             # read parf data from profile
78 0           _readICCparf($self, @_);
79              
80             # bless object
81 0           bless($self, $class);
82              
83             # return object reference
84 0           return($self);
85              
86             }
87              
88             # writes parf tag object to ICC profile
89             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
90             sub write_fh {
91              
92             # verify 4 parameters
93 0 0   0 0   (@_ == 4) or croak('wrong number of parameters');
94              
95             # write parf data to profile
96 0           goto &_writeICCparf;
97              
98             }
99              
100             # get tag size (for writing to profile)
101             # returns: (tag_size)
102             sub size {
103              
104             # get parameters
105 0     0 0   my ($self) = @_;
106              
107             # return size
108 0           return(12 + 4 * $Np[$self->[1][0]]);
109              
110             }
111              
112             # compute curve function
113             # parameters: (input_value)
114             # returns: (output_value)
115             sub transform {
116              
117             # get parameters
118 0     0 0   my ($self, $in) = @_;
119              
120             # local variables
121 0           my ($a, $type, $pow);
122              
123             # get parameter array
124 0           $a = $self->[1];
125              
126             # get function type
127 0           $type = $a->[0];
128              
129             # function type 0
130 0 0         if ($type == 0) {
    0          
    0          
    0          
    0          
    0          
131            
132             # return value Y = (aX + b)**γ + c
133 0           return(($a->[2] * $in + $a->[3])**$a->[1] + $a->[4]);
134            
135             # function type 1
136             } elsif ($type == 1) {
137            
138             # return value Y = a log10(bX**γ + c) + d
139 0           return($a->[2] * POSIX::log10($a->[3] * $in**$a->[1] + $a->[4]) + $a->[5]);
140            
141             # function type 2
142             } elsif ($type == 2) {
143            
144             # return value Y = ab**(cX+d) + e
145 0           return($a->[1] * $a->[2]**($a->[3] * $in + $a->[4]) + $a->[5]);
146            
147             # function type 3
148             } elsif ($type == 3) {
149            
150             # return value Y = (aX + b)/(cX + 1)
151 0           return(($a->[1] * $in + $a->[2])/($a->[3] * $in + 1));
152            
153             # function type 4
154             } elsif ($type == 4) {
155            
156             # return value Y = (aX + b)/(cX**γ + 1)**(1/γ)
157 0           return(($a->[2] * $in + $a->[3])/($a->[4] * $in**$a->[1] + 1)**(1/$a->[1]));
158            
159             # function type 5
160             } elsif ($type == 5) {
161            
162             # compute X**γ
163 0           $pow = $in**$a->[1];
164            
165             # return value Y = (aX**γ + b)/(cX**γ + 1)
166 0           return(($a->[2] * $pow + $a->[3])/($a->[4] * $pow + 1));
167            
168             } else {
169            
170             # error
171 0           croak('invalid parametric function type');
172            
173             }
174            
175             }
176              
177             # compute curve inverse
178             # parameters: (input_value)
179             # returns: (output_value)
180             sub inverse {
181              
182             # get parameters
183 0     0 0   my ($self, $in) = @_;
184              
185             # local variables
186 0           my ($a, $type);
187              
188             # get parameter array
189 0           $a = $self->[1];
190              
191             # get function type
192 0           $type = $a->[0];
193              
194             # function type 0
195 0 0         if ($type == 0) {
    0          
    0          
    0          
    0          
    0          
196            
197             # return value X = ((Y - c)**(1/γ) - b)/a
198 0           return((($in - $a->[4])**(1/$a->[1]) - $a->[3])/$a->[2]);
199            
200             # function type 1
201             } elsif ($type == 1) {
202            
203             # return value X = ((10**(Y/a - d/a) - c)/b)**(1/γ)
204 0           return(((10**($in/$a->[2] - $a->[5]/$a->[2]) - $a->[4])/$a->[3])**(1/$a->[1]));
205            
206             # function type 2
207             } elsif ($type == 2) {
208            
209             # return value X = (log((Y - e)/a)/log(b) - d)/c
210 0           return((log(($in - $a->[5])/$a->[1])/log($a->[2]) - $a->[4])/$a->[3]);
211            
212             # function type 3
213             } elsif ($type == 3) {
214            
215             # return value X = (b - Y)/(cY - a)
216 0           return(($a->[2] - $in)/($a->[3] * $in - $a->[1]));
217            
218             # function type 4
219             } elsif ($type == 4) {
220            
221             # error
222 0           croak('inverse of function type 4 requires numerical solution');
223            
224             # function type 5
225             } elsif ($type == 5) {
226            
227             # return value X = ((b - Y)/(cY - a))**(1/γ)
228 0           return((($a->[3] - $in)/($a->[4] * $in - $a->[2]))**(1/$a->[1]));
229            
230             } else {
231            
232             # error
233 0           croak('invalid parametric function type');
234            
235             }
236            
237             }
238              
239             # compute curve derivative
240             # parameters: (input_value)
241             # returns: (derivative_value)
242             sub derivative {
243              
244             # get parameters
245 0     0 0   my ($self, $in) = @_;
246              
247             # local variables
248 0           my ($a, $type, $den);
249              
250             # get parameter array
251 0           $a = $self->[1];
252              
253             # get function type
254 0           $type = $a->[0];
255              
256             # function type 0
257 0 0         if ($type == 0) {
    0          
    0          
    0          
    0          
    0          
258            
259             # return dY/dX = aγ(aX + b)**(γ - 1)
260 0 0         return($a->[1] == 1 ? $a->[2] : $a->[2] * $a->[1] * ($a->[2] * $in + $a->[3])**($a->[1] - 1));
261            
262             # function type 1
263             } elsif ($type == 1) {
264            
265             # compute denominator = ln(10) (bX**γ + c)
266 0           $den = ICC::Shared::ln10 * ($a->[3] * $in**$a->[1] + $a->[4]);
267            
268             # return dY/dX = abγX**(γ - 1)/(ln(10) (bX**γ + c))
269 0 0         return($den == 0 ? 'inf' : $a->[2] * $a->[3] * $a->[1] * $in**($a->[1] - 1)/$den);
270            
271             # function type 2
272             } elsif ($type == 2) {
273            
274             # return dY/dX = ac ln(b) b**(cX+d)
275 0           return($a->[1] * $a->[3] * log($a->[2]) * $a->[2]**($a->[3] * $in + $a->[4]));
276            
277             # function type 3
278             } elsif ($type == 3) {
279            
280             # compute denominator = (cX + 1)**2
281 0           $den = ($a->[3] * $in + 1)**2;
282            
283             # return value Y = (a - bc)/(cX + 1)**2
284 0 0         return($den == 0 ? 'inf' : ($a->[1] - $a->[2] * $a->[3])/$den);
285            
286             # function type 4
287             } elsif ($type == 4) {
288            
289             # compute denominator = (cX**γ + 1)
290 0           $den = ($a->[4] * $in**$a->[1] + 1);
291            
292             # return value Y = (a - (aX + b) cX**(γ - 1)/((cX**γ + 1)))/(cX**γ + 1)**(1/γ)
293 0 0         return($den == 0 ? 'inf' : ($a->[2] - ($a->[2] * $in + $a->[3]) * $a->[4] * $in**($a->[1] - 1)/$den)/$den**(1/$a->[1]));
294            
295             # function type 5
296             } elsif ($type == 5) {
297            
298             # compute denominator = (cX**γ + 1)**2
299 0           $den = ($a->[4] * $in**$a->[1] + 1)**2;
300            
301             # return value Y = γX**(γ - 1)(a - bc)/(cX**γ + 1)**2
302 0 0         return($den == 0 ? 'inf' : $a->[1] * $in**($a->[1] - 1) * ($a->[2] - $a->[3] * $a->[4])/$den);
303            
304             } else {
305            
306             # error
307 0           croak('invalid parametric function type');
308            
309             }
310            
311             }
312              
313             # get/set array reference
314             # parameters: ([ref_to_array])
315             # returns: (ref_to_array)
316             sub array {
317              
318             # get object reference
319 0     0 0   my $self = shift();
320            
321             # local variables
322 0           my ($array, $type);
323            
324             # if parameter
325 0 0         if (@_) {
326            
327             # get array reference
328 0           $array = shift();
329            
330             # verify array reference
331 0 0         (ref($array) eq 'ARRAY') or croak('not an array reference');
332            
333             # get function type
334 0           $type = $array->[0];
335            
336             # verify function type (integer, 0 - 5)
337 0 0 0       ($type == int($type) && $type >= 0 && $type <= 5) or croak('invalid function type');
      0        
338            
339             # verify number of parameters
340 0 0         ($#{$array} == $Np[$type]) or croak('wrong number of parameters');
  0            
341            
342             # set array reference
343 0           $self->[1] = $array;
344            
345             }
346            
347             # return array reference
348 0           return($self->[1]);
349              
350             }
351              
352             # print object contents to string
353             # format is an array structure
354             # parameter: ([format])
355             # returns: (string)
356             sub sdump {
357              
358             # get parameters
359 0     0 1   my ($self, $p) = @_;
360              
361             # local variables
362 0           my ($s, $fmt, $type);
363              
364             # resolve parameter to an array reference
365 0 0         $p = defined($p) ? ref($p) eq 'ARRAY' ? $p : [$p] : [];
    0          
366              
367             # get format string
368 0 0 0       $fmt = defined($p->[0]) && ! ref($p->[0]) ? $p->[0] : 'undef';
369              
370             # set string to object ID
371 0           $s = sprintf("'%s' object, (0x%x)\n", ref($self), $self);
372              
373             # if object has parameters
374 0 0         if (defined($type = $self->[1][0])) {
375            
376             # if function type 0, 4, 5
377 0 0 0       if ($type == 0 || $type == 4 || $type == 5) {
    0 0        
    0          
    0          
378            
379             # append parameter string
380 0           $s .= sprintf(" function type %d, gamma %.3f, a %.3f, b %.3f, c %.3f\n", @{$self->[1]});
  0            
381            
382             # if function type 1
383             } elsif ($type == 1) {
384            
385             # append parameter string
386 0           $s .= sprintf(" function type %d, gamma %.3f, a %.3f, b %.3f, c %.3f, d %.3f\n", @{$self->[1]});
  0            
387            
388             # if function type 2
389             } elsif ($type == 2) {
390            
391             # append parameter string
392 0           $s .= sprintf(" function type %d, gamma %.3f, a %.3f, b %.3f, c %.3f, d %.3f, e %.3f\n", @{$self->[1]});
  0            
393            
394             # if function type 3
395             } elsif ($type == 3) {
396            
397             # append parameter string
398 0           $s .= sprintf(" function type %d, a %.3f, b %.3f, c %.3f\n", @{$self->[1]});
  0            
399            
400             } else {
401            
402             # append error string
403 0           $s .= " invalid function type\n";
404            
405             }
406            
407             } else {
408            
409             # append string
410 0           $s .= " \n";
411            
412             }
413              
414             # return
415 0           return($s);
416              
417             }
418              
419             # read parf tag from ICC profile
420             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
421             sub _readICCparf {
422              
423             # get parameters
424 0     0     my ($self, $parent, $fh, $tag) = @_;
425              
426             # local variables
427 0           my ($buf, $fun, $cnt);
428              
429             # save tag signature
430 0           $self->[0]{'signature'} = $tag->[0];
431              
432             # seek start of tag
433 0           seek($fh, $tag->[1], 0);
434              
435             # read tag type signature and function type
436 0           read($fh, $buf, 12);
437              
438             # unpack function type
439 0           $fun = unpack('x8 n x2', $buf);
440              
441             # get parameter count and verify
442 0 0         defined($cnt = $Np[$fun]) or croak('invalid function type when reading \'parf\' tag');
443              
444             # read parameter values
445 0           read($fh, $buf, $cnt * 4);
446              
447             # unpack the values
448 0           $self->[1] = [$fun, unpack('f>*', $buf)];
449              
450             }
451              
452             # write parf tag to ICC profile
453             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
454             sub _writeICCparf {
455              
456             # get parameters
457 0     0     my ($self, $parent, $fh, $tag) = @_;
458              
459             # verify object structure
460 0 0 0       ($self->[1][0] == int($self->[1][0]) && $self->[1][0] >= 0 && $self->[1][0] <= 2 && $Np[$self->[1][0]] == $#{$self->[1]}) or croak('invalid function data when writing \'parf\' tag');
  0   0        
      0        
461              
462             # seek start of tag
463 0           seek($fh, $tag->[1], 0);
464              
465             # write tag
466 0           print $fh pack('a4 x4 n x2 f>*', 'parf', @{$self->[1]});
  0            
467              
468             }
469              
470             1;