File Coverage

blib/lib/ICC/Profile/parf.pm
Criterion Covered Total %
statement 9 115 7.8
branch 0 86 0.0
condition 0 27 0.0
subroutine 3 14 21.4
pod 1 9 11.1
total 13 251 5.1


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