File Coverage

blib/lib/Image/ExifTool/OpenEXR.pm
Criterion Covered Total %
statement 59 96 61.4
branch 27 62 43.5
condition 7 15 46.6
subroutine 5 5 100.0
pod 0 1 0.0
total 98 179 54.7


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: OpenEXR.pm
3             #
4             # Description: Read OpenEXR meta information
5             #
6             # Revisions: 2011/12/10 - P. Harvey Created
7             #
8             # References: 1) http://www.openexr.com/
9             #------------------------------------------------------------------------------
10              
11             package Image::ExifTool::OpenEXR;
12              
13 1     1   3611 use strict;
  1         2  
  1         31  
14 1     1   4 use vars qw($VERSION);
  1         2  
  1         48  
15 1     1   5 use Image::ExifTool qw(:DataAccess :Utils);
  1         2  
  1         236  
16 1     1   370 use Image::ExifTool::GPS;
  1         5  
  1         1178  
17              
18             $VERSION = '1.03';
19              
20             # supported EXR value format types (other types are extracted as undef binary data)
21             my %formatType = (
22             box2f => 'float[4]',
23             box2i => 'int32s[4]',
24             chlist => 1,
25             chromaticities => 'float[8]',
26             compression => 'int8u',
27             double => 'double',
28             envmap => 'int8u',
29             float => 'float',
30             'int' => 'int32s',
31             keycode => 'int32s[7]',
32             lineOrder => 'int8u',
33             m33f => 'float[9]',
34             m44f => 'float[16]',
35             rational => 'rational64s',
36             string => 'string', # incorrect in specification! (no leading int)
37             stringvector => 1,
38             tiledesc => 1,
39             timecode => 'int32u[2]',
40             v2f => 'float[2]',
41             v2i => 'int32s[2]',
42             v3f => 'float[3]',
43             v3i => 'int32s[3]',
44             );
45              
46             # OpenEXR tags
47             %Image::ExifTool::OpenEXR::Main = (
48             GROUPS => { 2 => 'Image' },
49             NOTES => q{
50             Information extracted from EXR images. See L for
51             the official specification.
52             },
53             _ver => { Name => 'EXRVersion' },
54             _lay => {
55             Name => 'Layout',
56             PrintHex => 1,
57             PrintConv => { 0 => 'Scan Lines', 0x200 => 'Tiles' },
58             },
59             adoptedNeutral => { },
60             altitude => {
61             Name => 'GPSAltitude',
62             Groups => { 2 => 'Location' },
63             PrintConv => q{
64             $val = int($val * 10) / 10;
65             return(($val =~ s/^-// ? "$val m Below" : "$val m Above") . " Sea Level");
66             },
67             },
68             aperture => { PrintConv => 'sprintf("%.1f",$val)' },
69             channels => { },
70             chromaticities => { },
71             capDate => {
72             Name => 'DateTimeOriginal',
73             Description => 'Date/Time Original',
74             Groups => { 2 => 'Time' },
75             PrintConv => '$self->ConvertDateTime($val)',
76             },
77             comments => { },
78             compression => {
79             PrintConvColumns => 2,
80             PrintConv => {
81             0 => 'None',
82             1 => 'RLE',
83             2 => 'ZIPS',
84             3 => 'ZIP',
85             4 => 'PIZ',
86             5 => 'PXR24',
87             6 => 'B44',
88             7 => 'B44A',
89             },
90             },
91             dataWindow => { },
92             displayWindow => { },
93             envmap => {
94             Name => 'EnvironmentMap',
95             PrintConv => {
96             0 => 'Latitude/Longitude',
97             1 => 'Cube',
98             },
99             },
100             expTime => {
101             Name => 'ExposureTime',
102             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
103             },
104             focus => {
105             Name => 'FocusDistance',
106             PrintConv => '"$val m"',
107             },
108             framesPerSecond => { },
109             keyCode => { },
110             isoSpeed => { Name => 'ISO' },
111             latitude => {
112             Name => 'GPSLatitude',
113             Groups => { 2 => 'Location' },
114             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
115             },
116             lineOrder => {
117             PrintConv => {
118             0 => 'Increasing Y',
119             1 => 'Decreasing Y',
120             2 => 'Random Y',
121             },
122             },
123             longitude => {
124             Name => 'GPSLongitude',
125             Groups => { 2 => 'Location' },
126             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
127             },
128             lookModTransform => { },
129             multiView => { },
130             owner => { Groups => { 2 => 'Author' } },
131             pixelAspectRatio => { },
132             preview => { },
133             renderingTransform => { },
134             screenWindowCenter => { },
135             screenWindowWidth => { },
136             tiles => { },
137             timeCode => { },
138             utcOffset => {
139             Name => 'TimeZone',
140             Groups => { 2 => 'Time' },
141             PrintConv => 'TimeZoneString($val / 60)',
142             },
143             whiteLuminance => { },
144             worldToCamera => { },
145             worldToNDC => { },
146             wrapmodes => { Name => 'WrapModes' },
147             xDensity => { Name => 'XResolution' },
148             # also observed:
149             # ilut
150             );
151              
152             #------------------------------------------------------------------------------
153             # Extract information from an OpenEXR file
154             # Inputs: 0) ExifTool object reference, 1) DirInfo reference
155             # Returns: 1 on success, 0 if this wasn't a valid OpenEXR file
156             sub ProcessEXR($$)
157             {
158 1     1 0 3 my ($et, $dirInfo) = @_;
159 1         3 my $raf = $$dirInfo{RAF};
160 1         3 my $verbose = $et->Options('Verbose');
161 1   33     3 my $binary = $et->Options('Binary') || $verbose;
162 1         3 my ($buff, $dim);
163              
164             # verify this is a valid RIFF file
165 1 50       3 return 0 unless $raf->Read($buff, 8) == 8;
166 1 50       6 return 0 unless $buff =~ /^\x76\x2f\x31\x01/s;
167 1         12 $et->SetFileType();
168 1         4 SetByteOrder('II');
169 1         2 my $tagTablePtr = GetTagTable('Image::ExifTool::OpenEXR::Main');
170              
171             # extract information from header
172 1         4 my $ver = unpack('x4V', $buff);
173 1         5 $et->HandleTag($tagTablePtr, '_ver', $ver & 0xff);
174 1         4 $et->HandleTag($tagTablePtr, '_lay', $ver & 0x200);
175 1 50       4 my $maxLen = ($ver & 0x400) ? 255 : 31;
176              
177             # extract attributes
178 1         2 for (;;) {
179 9 50       22 $raf->Read($buff, 68) or last;
180 9 100       22 last if $buff =~ /^\0/;
181 8 50       84 unless ($buff =~ /^([^\0]{1,$maxLen})\0([^\0]{1,$maxLen})\0(.{4})/sg) {
182 0         0 $et->Warn('EXR format error');
183 0         0 last;
184             }
185 8         35 my ($tag, $type, $size) = ($1, $2, unpack('V', $3));
186 8 50       25 unless ($raf->Seek(pos($buff) - length($buff), 1)) {
187 0         0 $et->Warn('Seek error');
188 0         0 last;
189             }
190 8         31 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
191 8 50       16 unless ($tagInfo) {
192 0         0 my $name = ucfirst $tag;
193 0         0 $name =~ s/([^a-zA-Z])([a-z])/$1\U$2/g; # capitalize first letter of each word
194 0         0 $name =~ tr/-_a-zA-Z0-9//dc;
195 0 0       0 if (length $name <= 1) {
196 0 0       0 if (length $name) {
197 0         0 $name = "Tag$name";
198             } else {
199 0         0 $name = 'Invalid';
200             }
201             }
202 0         0 $tagInfo = { Name => $name };
203 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
204 0         0 $et->VPrint(0, $$et{INDENT}, "[adding $tag]\n");
205             }
206 8         11 my ($val, $success);
207 8         16 my $format = $formatType{$type};
208 8 50 33     15 if ($format or $binary) {
209 8 50       18 $raf->Read($buff, $size) == $size and $success = 1;
210 8 50       23 if (not $format) {
    100          
    50          
    50          
    0          
211 0         0 $val = \$buff; # treat as undef binary data
212             } elsif ($format ne '1') {
213             # handle formats which map nicely into ExifTool format codes
214 7 50       46 if ($format =~ /^(\w+)\[?(\d*)/) {
215 7         29 my ($fmt, $cnt) = ($1, $2);
216 7 50       46 $cnt = $fmt eq 'string' ? $size : 1 unless $cnt;
    100          
217 7         22 $val = ReadValue(\$buff, 0, $fmt, $cnt, $size);
218             }
219             # handle other format types
220             } elsif ($type eq 'tiledesc') {
221 0 0       0 if ($size >= 9) {
222 0         0 my $x = Get32u(\$buff, 0);
223 0         0 my $y = Get32u(\$buff, 4);
224 0         0 my $mode = Get8u(\$buff, 8);
225 0         0 my $lvl = { 0 => 'One Level', 1 => 'MIMAP Levels', 2 => 'RIPMAP Levels' }->{$mode & 0x0f};
226 0 0       0 $lvl or $lvl = 'Unknown Levels (' . ($mode & 0xf) . ')';
227 0         0 my $rnd = { 0 => 'Round Down', 1 => 'Round Up' }->{$mode >> 4};
228 0 0       0 $rnd or $rnd = 'Unknown Rounding (' . ($mode >> 4) . ')';
229 0         0 $val = "${x}x$y; $lvl; $rnd";
230             }
231             } elsif ($type eq 'chlist') {
232 1         3 $val = [ ];
233 1         6 while ($buff =~ /\G([^\0]{1,31})\0(.{16})/sg) {
234 4         10 my ($str, $dat) = ($1, $2);
235 4         11 my ($pix,$lin,$x,$y) = unpack('VCx3VV', $dat);
236 4   33     15 $pix = { 0 => 'int8u', 1 => 'half', 2 => 'float' }->{$pix} || "unknown($pix)";
237 4 50       26 push @$val, "$str $pix" . ($lin ? ' linear' : '') . " $x $y";
238             }
239             } elsif ($type eq 'stringvector') {
240 0         0 $val = [ ];
241 0         0 my $pos = 0;
242 0         0 while ($pos + 4 <= length($buff)) {
243 0         0 my $len = Get32u(\$buff, $pos);
244 0 0       0 last if $pos + 4 + $len > length($buff);
245 0         0 push @$val, substr($buff, $pos + 4, $len);
246 0         0 $pos += 4 + $len;
247             }
248             } else {
249 0         0 $val = \$buff; # (shouldn't happen)
250             }
251             } else {
252             # avoid loading binary data
253 0         0 $val = \ "Binary data $size bytes";
254 0         0 $success = $raf->Seek($size, 1);
255             }
256 8 50       15 unless ($success) {
257 0         0 $et->Warn('Truncated or corrupted EXR file');
258 0         0 last;
259             }
260 8 50       16 $val = '' unless defined $val;
261              
262             # take image dimensions from dataWindow (with displayWindow as backup)
263 8 100 66     56 if (($tag eq 'dataWindow' or (not $dim and $tag eq 'displayWindow')) and
      66        
264             $val =~ /^(-?\d+) (-?\d+) (-?\d+) (-?\d+)$/)
265             {
266 1         12 $dim = [$3 - $1 + 1, $4 - $2 + 1];
267             }
268 8 50       18 if ($verbose) {
269 0 0       0 my $dataPt = ref $val ? $val : \$val,
270             $et->VerboseInfo($tag, $tagInfo,
271             Table => $tagTablePtr,
272             Value => $val,
273             Size => $size,
274             Format => $type,
275             DataPt => \$buff,
276             Addr => $raf->Tell() - $size,
277             );
278             }
279 8         30 $et->FoundTag($tagInfo, $val);
280             }
281 1 50       3 if ($dim) {
282 1         4 $et->FoundTag('ImageWidth', $$dim[0]);
283 1         11 $et->FoundTag('ImageHeight', $$dim[1]);
284             }
285 1         5 return 1;
286             }
287              
288             1; # end
289              
290             __END__