File Coverage

lib/ICC/Profile/samf.pm
Criterion Covered Total %
statement 15 95 15.7
branch 0 42 0.0
condition 0 3 0.0
subroutine 5 15 33.3
pod 1 8 12.5
total 21 163 12.8


line stmt bran cond sub pod time code
1             package ICC::Profile::samf;
2              
3 1     1   6 use strict;
  1         1  
  1         24  
4 1     1   4 use Carp;
  1         2  
  1         53  
5              
6             our $VERSION = 0.11;
7              
8             # revised 2018-08-07
9             #
10             # Copyright © 2004-2019 by William B. Birkett
11              
12             # add development directory
13 1     1   6 use lib 'lib';
  1         1  
  1         4  
14              
15             # inherit from Shared
16 1     1   99 use parent qw(ICC::Shared);
  1         2  
  1         4  
17              
18             # use POSIX math
19 1     1   43 use POSIX ();
  1         2  
  1         772  
20              
21             # create new samf 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 samf object
30 0           my $self = [
31             {}, # object header
32             [] # curve 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             # copy array
42 0           $self->[1] = [@{shift()}];
  0            
43            
44             }
45              
46             # bless object
47 0           bless($self, $class);
48              
49             # return object reference
50 0           return($self);
51              
52             }
53              
54             # create samf tag object from ICC profile
55             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
56             # returns: (ref_to_object)
57             sub new_fh {
58              
59             # get object class
60 0     0 0   my $class = shift();
61              
62             # create empty samf object
63 0           my $self = [
64             {}, # object header
65             [] # curve array
66             ];
67              
68             # verify 3 parameters
69 0 0         (@_ == 3) or croak('wrong number of parameters');
70              
71             # read samf data from profile
72 0           _readICCsamf($self, @_);
73              
74             # bless object
75 0           bless($self, $class);
76              
77             # return object reference
78 0           return($self);
79              
80             }
81              
82             # writes samf tag object to ICC profile
83             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
84             sub write_fh {
85              
86             # verify 4 parameters
87 0 0   0 0   (@_ == 4) or croak('wrong number of parameters');
88              
89             # write samf data to profile
90 0           goto &_writeICCsamf;
91              
92             }
93              
94             # get tag size (for writing to profile)
95             # returns: (tag_size)
96             sub size {
97              
98             # get parameters
99 0     0 0   my ($self) = @_;
100              
101             # return size
102 0           return(12 + 4 * @{$self->[1]});
  0            
103              
104             }
105              
106             # compute curve derivative function
107             # parameters: (input_value, lower_breakpoint, upper_breakpoint, preceeding_segment_object)
108             # returns: (output_value)
109             sub derivative {
110              
111             # get parameters
112 0     0 0   my ($self, $in, $xbp0, $xbp1, $pseg) = @_;
113              
114             # local variables
115 0           my ($xpos, $ix, $ir, $low);
116              
117             # compute x-position (0 - number of curve entries)
118 0           $xpos = ($#{$self->[1]} + 1) * ($in - $xbp0)/($xbp1 - $xbp0);
  0            
119              
120             # compute lower array index
121 0           $ix = POSIX::floor($xpos);
122              
123             # limit lower array index
124 0 0         $ix = $ix < 0 ? 0 : $ix > $#{$self->[1]} ? $#{$self->[1]} : $ix;
  0 0          
  0            
125              
126             # compute interpolation ratio
127 0           $ir = $xpos - $ix;
128              
129             # if lower breakpoint used
130 0 0         if ($ix == 0) {
131            
132             # if preceeding segment a 'parf' object
133 0 0         if (UNIVERSAL::isa($pseg, 'ICC::Profile::parf')) {
    0          
134            
135             # compute lower curve entry value
136 0           $low = $pseg->transform($xbp0);
137            
138             # if preceeding segment a 'samf' object
139             } elsif (UNIVERSAL::isa($pseg, 'ICC::Profile::samf')) {
140            
141             # get lower curve entry value
142 0           $low = $pseg->[1][-1];
143            
144             }
145            
146             } else {
147            
148             # get lower curve entry value
149 0           $low = $self->[1][$ix - 1];
150            
151             }
152              
153             # return derivative value
154 0           return(($#{$self->[1]} + 1) * ($self->[1][$ix] - $low)/($xbp1 - $xbp0));
  0            
155              
156             }
157              
158             # compute curve function
159             # parameters: (input_value, lower_breakpoint, upper_breakpoint, preceeding_segment_object)
160             # returns: (output_value)
161             sub transform {
162              
163             # get parameters
164 0     0 0   my ($self, $in, $xbp0, $xbp1, $pseg) = @_;
165              
166             # local variables
167 0           my ($xpos, $ix, $ir, $low);
168              
169             # compute x-position (0 - number of curve entries)
170 0           $xpos = ($#{$self->[1]} + 1) * ($in - $xbp0)/($xbp1 - $xbp0);
  0            
171              
172             # compute lower array index
173 0           $ix = POSIX::floor($xpos);
174              
175             # limit lower array index
176 0 0         $ix = $ix < 0 ? 0 : $ix > $#{$self->[1]} ? $#{$self->[1]} : $ix;
  0 0          
  0            
177              
178             # compute interpolation ratio
179 0           $ir = $xpos - $ix;
180              
181             # if lower breakpoint used
182 0 0         if ($ix == 0) {
183            
184             # if preceeding segment a 'parf' object
185 0 0         if (UNIVERSAL::isa($pseg, 'ICC::Profile::parf')) {
    0          
186            
187             # compute lower curve entry value
188 0           $low = $pseg->transform($xbp0);
189            
190             # if preceeding segment a 'samf' object
191             } elsif (UNIVERSAL::isa($pseg, 'ICC::Profile::samf')) {
192            
193             # get lower curve entry value
194 0           $low = $pseg->[1][-1];
195            
196             }
197            
198             } else {
199            
200             # get lower curve entry value
201 0           $low = $self->[1][$ix - 1];
202            
203             }
204              
205             # return interpolated value
206 0           return($low + $ir * ($self->[1][$ix] - $low));
207              
208             }
209              
210             # get/set array reference
211             # parameters: ([ref_to_array])
212             # returns: (ref_to_array)
213             sub array {
214              
215             # get object reference
216 0     0 0   my $self = shift();
217              
218             # if parameter
219 0 0         if (@_) {
220            
221             # verify array reference
222 0 0         (ref($_[0]) eq 'ARRAY') or croak('not an array reference');
223            
224             # set array reference
225 0           $self->[1] = shift();
226            
227             }
228              
229             # return array reference
230 0           return($self->[1]);
231              
232             }
233              
234             # print object contents to string
235             # format is an array structure
236             # parameter: ([format])
237             # returns: (string)
238             sub sdump {
239              
240             # get parameters
241 0     0 1   my ($self, $p) = @_;
242              
243             # local variables
244 0           my ($s, $fmt);
245              
246             # resolve parameter to an array reference
247 0 0         $p = defined($p) ? ref($p) eq 'ARRAY' ? $p : [$p] : [];
    0          
248              
249             # get format string
250 0 0 0       $fmt = defined($p->[0]) && ! ref($p->[0]) ? $p->[0] : 'undef';
251              
252             # set string to object ID
253 0           $s = sprintf("'%s' object, (0x%x)\n", ref($self), $self);
254              
255             # return
256 0           return($s);
257              
258             }
259              
260             # read samf tag from ICC profile
261             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
262             sub _readICCsamf {
263              
264             # get parameters
265 0     0     my ($self, $parent, $fh, $tag) = @_;
266              
267             # local variables
268 0           my ($buf, $cnt);
269              
270             # save tag signature
271 0           $self->[0]{'signature'} = $tag->[0];
272              
273             # seek start of tag
274 0           seek($fh, $tag->[1], 0);
275              
276             # read tag type signature and count
277 0           read($fh, $buf, 12);
278              
279             # unpack count
280 0           $cnt = unpack('x8 N', $buf);
281              
282             # if count > 0
283 0 0         if ($cnt > 0) {
284            
285             # read array values
286 0           read($fh, $buf, $cnt * 4);
287            
288             # unpack the values
289 0           $self->[1] = [unpack('f>*', $buf)];
290            
291             } else {
292            
293             # error
294 0           croak('\'samf\' tag has zero count');
295            
296             }
297            
298             }
299              
300             # write samf tag to ICC profile
301             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
302             sub _writeICCsamf {
303              
304             # get parameters
305 0     0     my ($self, $parent, $fh, $tag) = @_;
306              
307             # seek start of tag
308 0           seek($fh, $tag->[1], 0);
309              
310             # write tag type signature and count
311 0           print $fh pack('a4 x4 N', 'samf', scalar(@{$self->[1]}));
  0            
312              
313             # if count > 0
314 0 0         if (@{$self->[1]} > 0) {
  0            
315            
316             # write array
317 0           print $fh pack('f>*', @{$self->[1]});
  0            
318            
319             } else {
320            
321             # error
322 0           croak('\'samf\' object has zero count');
323            
324             }
325            
326             }
327              
328             1;