File Coverage

blib/lib/Image/ExifTool/BMP.pm
Criterion Covered Total %
statement 27 43 62.7
branch 5 22 22.7
condition 8 33 24.2
subroutine 4 4 100.0
pod 0 1 0.0
total 44 103 42.7


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: BMP.pm
3             #
4             # Description: Read BMP meta information
5             #
6             # Revisions: 07/16/2005 - P. Harvey Created
7             #
8             # References: 1) http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
9             # 2) http://www.fourcc.org/rgb.php
10             # 3) https://msdn.microsoft.com/en-us/library/dd183381(v=vs.85).aspx
11             #------------------------------------------------------------------------------
12              
13             package Image::ExifTool::BMP;
14              
15 3     3   4521 use strict;
  3         7  
  3         109  
16 3     3   18 use vars qw($VERSION);
  3         7  
  3         161  
17 3     3   25 use Image::ExifTool qw(:DataAccess :Utils);
  3         5  
  3         3542  
18              
19             $VERSION = '1.09';
20              
21             # conversions for fixed-point 2.30 format values
22             my %fixed2_30 = (
23             ValueConv => q{
24             my @a = split ' ', $val;
25             $_ /= 0x40000000 foreach @a;
26             "@a";
27             },
28             PrintConv => q{
29             my @a = split ' ', $val;
30             $_ = sprintf('%.6f', $_) foreach @a;
31             "@a";
32             },
33             );
34              
35             # BMP chunks
36             %Image::ExifTool::BMP::Main = (
37             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
38             GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
39             NOTES => q{
40             There really isn't much meta information in a BMP file as such, just a bit
41             of image related information.
42             },
43             0 => {
44             Name => 'BMPVersion',
45             Format => 'int32u',
46             Notes => q{
47             this is actually the size of the BMP header, but used to determine the BMP
48             version
49             },
50             RawConv => '$$self{BMPVersion} = $val',
51             PrintConv => {
52             40 => 'Windows V3',
53             68 => 'AVI BMP structure?', #PH (seen in AVI movies from some Casio and Nikon cameras)
54             108 => 'Windows V4',
55             124 => 'Windows V5',
56             },
57             },
58             4 => {
59             Name => 'ImageWidth',
60             Format => 'int32u',
61             },
62             8 => {
63             Name => 'ImageHeight',
64             Format => 'int32s', # (negative when stored in top-to-bottom order)
65             ValueConv => 'abs($val)',
66             },
67             12 => {
68             Name => 'Planes',
69             Format => 'int16u',
70             # values: 0,1,4,8,16,24,32
71             },
72             14 => {
73             Name => 'BitDepth',
74             Format => 'int16u',
75             },
76             16 => {
77             Name => 'Compression',
78             Format => 'int32u',
79             RawConv => '$$self{BMPCompression} = $val',
80             # (formatted as string[4] for some values in AVI images)
81             ValueConv => '$val > 256 ? unpack("A4",pack("V",$val)) : $val',
82             PrintConv => {
83             0 => 'None',
84             1 => '8-Bit RLE',
85             2 => '4-Bit RLE',
86             3 => 'Bitfields',
87             4 => 'JPEG', #2
88             5 => 'PNG', #2
89             # pass through ASCII video compression codec ID's
90             OTHER => sub {
91             my $val = shift;
92             # convert non-ascii characters
93             $val =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/eg;
94             return $val;
95             },
96             },
97             },
98             20 => {
99             Name => 'ImageLength',
100             Format => 'int32u',
101             RawConv => '$$self{BMPImageLength} = $val',
102             },
103             24 => {
104             Name => 'PixelsPerMeterX',
105             Format => 'int32u',
106             },
107             28 => {
108             Name => 'PixelsPerMeterY',
109             Format => 'int32u',
110             },
111             32 => {
112             Name => 'NumColors',
113             Format => 'int32u',
114             PrintConv => '$val ? $val : "Use BitDepth"',
115             },
116             36 => {
117             Name => 'NumImportantColors',
118             Format => 'int32u',
119             Hook => '$varSize += $size if $$self{BMPVersion} == 68', # (the rest is invalid for AVI BMP's)
120             PrintConv => '$val ? $val : "All"',
121             },
122             40 => {
123             Name => 'RedMask',
124             Format => 'int32u',
125             PrintConv => 'sprintf("0x%.8x",$val)',
126             },
127             44 => {
128             Name => 'GreenMask',
129             Format => 'int32u',
130             PrintConv => 'sprintf("0x%.8x",$val)',
131             },
132             48 => {
133             Name => 'BlueMask',
134             Format => 'int32u',
135             PrintConv => 'sprintf("0x%.8x",$val)',
136             },
137             52 => {
138             Name => 'AlphaMask',
139             Format => 'int32u',
140             PrintConv => 'sprintf("0x%.8x",$val)',
141             },
142             56 => {
143             Name => 'ColorSpace',
144             Format => 'undef[4]',
145             RawConv => '$$self{BMPColorSpace} = $val =~ /\0/ ? Get32u(\$val, 0) : pack("N",unpack("V",$val))',
146             PrintConv => {
147             0 => 'Calibrated RGB',
148             1 => 'Device RGB',
149             2 => 'Device CMYK',
150             LINK => 'Linked Color Profile',
151             MBED => 'Embedded Color Profile',
152             sRGB => 'sRGB',
153             'Win ' => 'Windows Color Space',
154             },
155             },
156             60 => {
157             Name => 'RedEndpoint',
158             Condition => '$$self{BMPColorSpace} eq "0"',
159             Format => 'int32u[3]',
160             %fixed2_30,
161             },
162             72 => {
163             Name => 'GreenEndpoint',
164             Condition => '$$self{BMPColorSpace} eq "0"',
165             Format => 'int32u[3]',
166             %fixed2_30,
167             },
168             84 => {
169             Name => 'BlueEndpoint',
170             Condition => '$$self{BMPColorSpace} eq "0"',
171             Format => 'int32u[3]',
172             %fixed2_30,
173             },
174             96 => {
175             Name => 'GammaRed',
176             Condition => '$$self{BMPColorSpace} eq "0"',
177             Format => 'fixed32u',
178             },
179             100 => {
180             Name => 'GammaGreen',
181             Condition => '$$self{BMPColorSpace} eq "0"',
182             Format => 'fixed32u',
183             },
184             104 => {
185             Name => 'GammaBlue',
186             Condition => '$$self{BMPColorSpace} eq "0"',
187             Format => 'fixed32u',
188             },
189             108 => {
190             Name => 'RenderingIntent',
191             Format => 'int32u',
192             PrintConv => {
193             1 => 'Graphic (LCS_GM_BUSINESS)',
194             2 => 'Proof (LCS_GM_GRAPHICS)',
195             4 => 'Picture (LCS_GM_IMAGES)',
196             8 => 'Absolute Colorimetric (LCS_GM_ABS_COLORIMETRIC)',
197             },
198             },
199             112 => {
200             Name => 'ProfileDataOffset',
201             Condition => '$$self{BMPColorSpace} eq "LINK" or $$self{BMPColorSpace} eq "MBED"',
202             Format => 'int32u',
203             RawConv => '$$self{BMPProfileOffset} = $val',
204             },
205             116 => {
206             Name => 'ProfileSize',
207             Condition => '$$self{BMPColorSpace} eq "LINK" or $$self{BMPColorSpace} eq "MBED"',
208             Format => 'int32u',
209             RawConv => '$$self{BMPProfileSize} = $val',
210             },
211             # 120 - reserved
212             );
213              
214             # OS/2 12-byte bitmap header (ref http://www.fileformat.info/format/bmp/egff.htm)
215             %Image::ExifTool::BMP::OS2 = (
216             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
217             GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
218             NOTES => 'Information extracted from OS/2-format BMP images.',
219             0 => {
220             Name => 'BMPVersion',
221             Format => 'int32u',
222             Notes => 'again, the header size is used to determine the BMP version',
223             PrintConv => {
224             12 => 'OS/2 V1',
225             64 => 'OS/2 V2',
226             },
227             },
228             4 => { Name => 'ImageWidth', Format => 'int16u' },
229             6 => { Name => 'ImageHeight', Format => 'int16u' },
230             8 => { Name => 'Planes', Format => 'int16u' },
231             10 => { Name => 'BitDepth', Format => 'int16u' },
232             );
233              
234             %Image::ExifTool::BMP::Extra = (
235             GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
236             NOTES => 'Extra information extracted from some BMP images.',
237             VARS => { NO_ID => 1 },
238             LinkedProfileName => { },
239             ICC_Profile => { SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' } },
240             EmbeddedJPG => {
241             Groups => { 2 => 'Preview' },
242             Binary => 1,
243             },
244             EmbeddedPNG => {
245             Groups => { 2 => 'Preview' },
246             Binary => 1,
247             },
248             );
249              
250             #------------------------------------------------------------------------------
251             # Extract EXIF information from a BMP image
252             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
253             # Returns: 1 on success, 0 if this wasn't a valid BMP file
254             sub ProcessBMP($$)
255             {
256 1     1 0 3 my ($et, $dirInfo) = @_;
257 1         3 my $raf = $$dirInfo{RAF};
258 1         2 my ($buff, $tagTablePtr);
259              
260             # verify this is a valid BMP file
261 1 50       5 return 0 unless $raf->Read($buff, 18) == 18;
262 1 50       14 return 0 unless $buff =~ /^BM/;
263 1         6 SetByteOrder('II');
264 1         4 my $len = Get32u(\$buff, 14);
265             # len = v1:12, v4:108, v5:124
266 1 50 33     21 return 0 unless $len == 12 or $len == 16 or ($len >= 40 and $len < 1000000);
      33        
      33        
267 1 50 33     4 return 0 unless $raf->Seek(-4, 1) and $raf->Read($buff, $len) == $len;
268 1         16 $et->SetFileType(); # set the FileType tag
269             #
270             # process the BMP header
271             #
272 1         16 my %dirInfo = (
273             DataPt => \$buff,
274             DirStart => 0,
275             DirLen => length($buff),
276             );
277 1 50 33     12 if ($len == 12 or $len == 16 or $len == 64) { # old OS/2 format BMP
      33        
278 0         0 $tagTablePtr = GetTagTable('Image::ExifTool::BMP::OS2');
279             } else {
280 1         11 $tagTablePtr = GetTagTable('Image::ExifTool::BMP::Main');
281             }
282 1         9 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
283             #
284             # extract any embedded images
285             #
286 1         4 my $extraTable = GetTagTable('Image::ExifTool::BMP::Extra');
287 1 0 33     6 if ($$et{BMPCompression} and $$et{BMPImageLength} and
      0        
      0        
288             ($$et{BMPCompression} == 4 or $$et{BMPCompression} == 5))
289             {
290 0 0       0 my $tag = $$et{BMPCompression} == 4 ? 'EmbeddedJPG' : 'EmbeddedPNG';
291 0         0 my $val = $et->ExtractBinary($raf->Tell(), $$et{BMPImageLength}, $tag);
292 0 0       0 if ($val) {
293 0         0 $et->HandleTag($extraTable, $tag, $val);
294             }
295             }
296             #
297             # process profile data if it exists (v5 header only)
298             #
299 1 0 33     5 if ($len == 124 and $$et{BMPProfileOffset}) {
300 0         0 my $pos = $$et{BMPProfileOffset} + 14; # (note the 14-byte shift!)
301 0         0 my $size = $$et{BMPProfileSize};
302 0 0 0     0 if ($raf->Seek($pos, 0) and $raf->Read($buff, $size) == $size) {
303 0         0 my $tag;
304 0 0       0 if ($$et{BMPColorSpace} eq 'LINK') {
305 0         0 $buff =~ s/\0+$//; # remove null terminator(s)
306 0         0 $buff = $et->Decode($buff, 'Latin'); # convert from Latin
307 0         0 $tag = 'LinkedProfileName';
308             } else {
309 0         0 $tag = 'ICC_Profile';
310             }
311 0         0 $et->HandleTag($extraTable, $tag => $buff, Size => $size, DataPos => $pos);
312             } else {
313 0         0 $et->Warn('Error loading profile data', 1);
314             }
315             }
316 1         5 return 1;
317             }
318              
319             1; # end
320              
321             __END__