File Coverage

blib/lib/ICC/Profile/samf.pm
Criterion Covered Total %
statement 12 92 13.0
branch 0 42 0.0
condition 0 3 0.0
subroutine 4 14 28.5
pod 1 8 12.5
total 17 159 10.6


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