File Coverage

blib/lib/Image/ExifTool/PanasonicRaw.pm
Criterion Covered Total %
statement 110 125 88.0
branch 22 42 52.3
condition 9 25 36.0
subroutine 10 10 100.0
pod 0 6 0.0
total 151 208 72.6


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: PanasonicRaw.pm
3             #
4             # Description: Read/write Panasonic/Leica RAW/RW2/RWL meta information
5             #
6             # Revisions: 2009/03/24 - P. Harvey Created
7             # 2009/05/12 - PH Added RWL file type (same format as RW2)
8             #
9             # References: 1) https://exiftool.org/forum/index.php/topic,1542.0.html
10             # 2) http://www.cybercom.net/~dcoffin/dcraw/
11             # 3) http://syscall.eu/#pana
12             # 4) Klaus Homeister private communication
13             # IB) Iliah Borg private communication (LibRaw)
14             # JD) Jens Duttke private communication (TZ3,FZ30,FZ50)
15             #------------------------------------------------------------------------------
16              
17             package Image::ExifTool::PanasonicRaw;
18              
19 14     14   102 use strict;
  14         29  
  14         477  
20 14     14   87 use vars qw($VERSION);
  14         31  
  14         613  
21 14     14   85 use Image::ExifTool qw(:DataAccess :Utils);
  14         34  
  14         2998  
22 14     14   91 use Image::ExifTool::Exif;
  14         31  
  14         29881  
23              
24             $VERSION = '1.25';
25              
26             sub ProcessJpgFromRaw($$$);
27             sub WriteJpgFromRaw($$$);
28             sub WriteDistortionInfo($$$);
29             sub ProcessDistortionInfo($$$);
30              
31             my %jpgFromRawMap = (
32             IFD1 => 'IFD0',
33             EXIF => 'IFD0', # to write EXIF as a block
34             ExifIFD => 'IFD0',
35             GPS => 'IFD0',
36             SubIFD => 'IFD0',
37             GlobParamIFD => 'IFD0',
38             PrintIM => 'IFD0',
39             InteropIFD => 'ExifIFD',
40             MakerNotes => 'ExifIFD',
41             IFD0 => 'APP1',
42             MakerNotes => 'ExifIFD',
43             Comment => 'COM',
44             );
45              
46             my %wbTypeInfo = (
47             PrintConv => \%Image::ExifTool::Exif::lightSource,
48             SeparateTable => 'EXIF LightSource',
49             );
50              
51             my %panasonicWhiteBalance = ( #forum9396
52             0 => 'Auto',
53             1 => 'Daylight',
54             2 => 'Cloudy',
55             3 => 'Tungsten',
56             4 => 'n/a',
57             5 => 'Flash',
58             6 => 'n/a',
59             7 => 'n/a',
60             8 => 'Custom#1',
61             9 => 'Custom#2',
62             10 => 'Custom#3',
63             11 => 'Custom#4',
64             12 => 'Shade',
65             13 => 'Kelvin',
66             16 => 'AWBc', # GH5 and G9 (Makernotes WB==19)
67             );
68              
69             # Tags found in Panasonic RAW/RW2/RWL images (ref PH)
70             %Image::ExifTool::PanasonicRaw::Main = (
71             GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'},
72             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
73             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
74             WRITE_GROUP => 'IFD0', # default write group
75             NOTES => 'These tags are found in IFD0 of Panasonic/Leica RAW, RW2 and RWL images.',
76             0x01 => {
77             Name => 'PanasonicRawVersion',
78             Writable => 'undef',
79             },
80             0x02 => 'SensorWidth', #1/PH
81             0x03 => 'SensorHeight', #1/PH
82             0x04 => 'SensorTopBorder', #JD
83             0x05 => 'SensorLeftBorder', #JD
84             0x06 => 'SensorBottomBorder', #PH
85             0x07 => 'SensorRightBorder', #PH
86             # observed values for unknown tags - PH
87             # 0x08: 1
88             # 0x09: 1,3,4
89             # 0x0a: 12
90             # (IB gave 0x08-0x0a as BlackLevel tags, but Klaus' decoding makes more sense)
91             0x08 => { Name => 'SamplesPerPixel', Writable => 'int16u', Protected => 1 }, #4
92             0x09 => { #4
93             Name => 'CFAPattern',
94             Writable => 'int16u',
95             Protected => 1,
96             PrintConv => {
97             0 => 'n/a',
98             1 => '[Red,Green][Green,Blue]', # (CM-1, FZ70)
99             2 => '[Green,Red][Blue,Green]', # (LX-7)
100             3 => '[Green,Blue][Red,Green]', # (ZS100, FZ2500, FZ1000, ...)
101             4 => '[Blue,Green][Green,Red]', # (LC-100, G-7, V-LUX1, ...)
102             },
103             },
104             0x0a => { Name => 'BitsPerSample', Writable => 'int16u', Protected => 1 }, #4
105             0x0b => { #4
106             Name => 'Compression',
107             Writable => 'int16u',
108             Protected => 1,
109             PrintConv => {
110             34316 => 'Panasonic RAW 1', # (most models - RAW/RW2/RWL)
111             34826 => 'Panasonic RAW 2', # (DIGILUX 2 - RAW)
112             34828 => 'Panasonic RAW 3', # (D-LUX2,D-LUX3,FZ30,LX1 - RAW)
113             34830 => 'Panasonic RAW 4', #IB (Leica DIGILUX 3, Panasonic DMC-L1)
114             },
115             },
116             # 0x0c: 2 (only Leica Digilux 2)
117             # 0x0d: 0,1
118             # 0x0e,0x0f,0x10: 4095
119             0x0e => { Name => 'LinearityLimitRed', Writable => 'int16u' }, #IB
120             0x0f => { Name => 'LinearityLimitGreen', Writable => 'int16u' }, #IB
121             0x10 => { Name => 'LinearityLimitBlue', Writable => 'int16u' }, #IB
122             0x11 => { #JD
123             Name => 'RedBalance',
124             Writable => 'int16u',
125             ValueConv => '$val / 256',
126             ValueConvInv => 'int($val * 256 + 0.5)',
127             Notes => 'found in Digilux 2 RAW images',
128             },
129             0x12 => { #JD
130             Name => 'BlueBalance',
131             Writable => 'int16u',
132             ValueConv => '$val / 256',
133             ValueConvInv => 'int($val * 256 + 0.5)',
134             },
135             0x13 => { #IB
136             Name => 'WBInfo',
137             SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo' },
138             },
139             0x17 => { #1
140             Name => 'ISO',
141             Writable => 'int16u',
142             },
143             # 0x18,0x19,0x1a: 0
144             0x18 => { #IB
145             Name => 'HighISOMultiplierRed',
146             Writable => 'int16u',
147             ValueConv => '$val / 256',
148             ValueConvInv => 'int($val * 256 + 0.5)',
149             },
150             0x19 => { #IB
151             Name => 'HighISOMultiplierGreen',
152             Writable => 'int16u',
153             ValueConv => '$val / 256',
154             ValueConvInv => 'int($val * 256 + 0.5)',
155             },
156             0x1a => { #IB
157             Name => 'HighISOMultiplierBlue',
158             Writable => 'int16u',
159             ValueConv => '$val / 256',
160             ValueConvInv => 'int($val * 256 + 0.5)',
161             },
162             # 0x1b: [binary data] (something to do with the camera ISO cababilities: int16u count N,
163             # followed by table of N entries: int16u ISO, int16u[3] RGB gains - ref IB)
164             0x1c => { Name => 'BlackLevelRed', Writable => 'int16u' }, #IB
165             0x1d => { Name => 'BlackLevelGreen', Writable => 'int16u' }, #IB
166             0x1e => { Name => 'BlackLevelBlue', Writable => 'int16u' }, #IB
167             0x24 => { #2
168             Name => 'WBRedLevel',
169             Writable => 'int16u',
170             },
171             0x25 => { #2
172             Name => 'WBGreenLevel',
173             Writable => 'int16u',
174             },
175             0x26 => { #2
176             Name => 'WBBlueLevel',
177             Writable => 'int16u',
178             },
179             0x27 => { #IB
180             Name => 'WBInfo2',
181             SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo2' },
182             },
183             # 0x27,0x29,0x2a,0x2b,0x2c: [binary data]
184             0x2d => { #IB
185             Name => 'RawFormat',
186             Writable => 'int16u',
187             Protected => 1,
188             # 2 - RAW DMC-FZ8/FZ18
189             # 3 - RAW DMC-L10
190             # 4 - RW2 for most other models, including G9 in "pixel shift off" mode and YUNEEC CGO4
191             # (must add 15 to black levels for RawFormat == 4)
192             # 5 - RW2 DC-GH5s; G9 in "pixel shift on" mode
193             # 6 - RW2 DC-S1, DC-S1r in "pixel shift off" mode
194             # 7 - RW2 DC-S1r (and probably DC-S1, have no raw samples) in "pixel shift on" mode
195             # not used - DMC-LX1/FZ30/FZ50/L1/LX1/LX2
196             # (modes 5 and 7 are lossless)
197             },
198             0x2e => { #JD
199             Name => 'JpgFromRaw', # (writable directory!)
200             Groups => { 2 => 'Preview' },
201             Writable => 'undef',
202             # protect this tag because it contains all the metadata
203             Flags => [ 'Binary', 'Protected', 'NestedHtmlDump', 'BlockExtract' ],
204             Notes => 'processed as an embedded document because it contains full EXIF',
205             WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)',
206             DataTag => 'JpgFromRaw',
207             RawConv => '$self->ValidateImage(\$val,$tag)',
208             SubDirectory => {
209             # extract information from embedded image since it is metadata-rich,
210             # unless HtmlDump option set (note that the offsets will be relative,
211             # not absolute like they should be in verbose mode)
212             TagTable => 'Image::ExifTool::JPEG::Main',
213             WriteProc => \&WriteJpgFromRaw,
214             ProcessProc => \&ProcessJpgFromRaw,
215             },
216             },
217             0x2f => { Name => 'CropTop', Writable => 'int16u' },
218             0x30 => { Name => 'CropLeft', Writable => 'int16u' },
219             0x31 => { Name => 'CropBottom', Writable => 'int16u' },
220             0x32 => { Name => 'CropRight', Writable => 'int16u' },
221             0x10f => {
222             Name => 'Make',
223             Groups => { 2 => 'Camera' },
224             Writable => 'string',
225             DataMember => 'Make',
226             # save this value as an ExifTool member variable
227             RawConv => '$self->{Make} = $val',
228             },
229             0x110 => {
230             Name => 'Model',
231             Description => 'Camera Model Name',
232             Groups => { 2 => 'Camera' },
233             Writable => 'string',
234             DataMember => 'Model',
235             # save this value as an ExifTool member variable
236             RawConv => '$self->{Model} = $val',
237             },
238             0x111 => {
239             Name => 'StripOffsets',
240             # (this value is 0xffffffff for some models, and RawDataOffset must be used)
241             Flags => [ 'IsOffset', 'PanasonicHack' ],
242             OffsetPair => 0x117, # point to associated byte counts
243             ValueConv => 'length($val) > 32 ? \$val : $val',
244             },
245             0x112 => {
246             Name => 'Orientation',
247             Writable => 'int16u',
248             PrintConv => \%Image::ExifTool::Exif::orientation,
249             Priority => 0, # so IFD1 doesn't take precedence
250             },
251             0x116 => {
252             Name => 'RowsPerStrip',
253             Priority => 0,
254             },
255             0x117 => {
256             Name => 'StripByteCounts',
257             # (note that this value may represent something like uncompressed byte count
258             # for RAW/RW2/RWL images from some models, and is zero for some other models)
259             OffsetPair => 0x111, # point to associated offset
260             ValueConv => 'length($val) > 32 ? \$val : $val',
261             },
262             0x118 => {
263             Name => 'RawDataOffset', #PH (RW2/RWL)
264             IsOffset => '$$et{TIFF_TYPE} =~ /^(RW2|RWL)$/', # (invalid in DNG-converted files)
265             PanasonicHack => 1,
266             OffsetPair => 0x117, # (use StripByteCounts as the offset pair)
267             NotRealPair => 1, # (to avoid Validate warning)
268             },
269             0x119 => {
270             Name => 'DistortionInfo',
271             SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::DistortionInfo' },
272             },
273             # 0x11b - chromatic aberration correction (ref 3) (also see forum9366)
274             0x11c => { #forum9373
275             Name => 'Gamma',
276             Writable => 'int16u',
277             # unfortunately it seems that the scaling factor varies with model...
278             ValueConv => '$val / ($val >= 1024 ? 1024 : ($val >= 256 ? 256 : 100))',
279             ValueConvInv => 'int($val * 256 + 0.5)',
280             },
281             0x120 => {
282             Name => 'CameraIFD',
283             SubDirectory => {
284             TagTable => 'Image::ExifTool::PanasonicRaw::CameraIFD',
285             Base => '$start',
286             ProcessProc => \&Image::ExifTool::ProcessTIFF,
287             },
288             },
289             0x121 => { #forum9295
290             Name => 'Multishot',
291             Writable => 'int32u',
292             PrintConv => {
293             0 => 'Off',
294             65536 => 'Pixel Shift',
295             },
296             },
297             # 0x122 - int32u: RAWDataOffset for the GH5s/GX9, or pointer to end of raw data for G9 (forum9295)
298             0x2bc => { # PH Extension!!
299             Name => 'ApplicationNotes', # (writable directory!)
300             Writable => 'int8u',
301             Format => 'undef',
302             Flags => [ 'Binary', 'Protected' ],
303             SubDirectory => {
304             DirName => 'XMP',
305             TagTable => 'Image::ExifTool::XMP::Main',
306             },
307             },
308             0x001b => { #forum9250
309             Name => 'NoiseReductionParams',
310             Writable => 'undef',
311             Format => 'int16u',
312             Count => -1,
313             Flags => 'Protected',
314             Notes => q{
315             the camera's default noise reduction setup. The first number is the number
316             of entries, then for each entry there are 4 numbers: an ISO speed, and
317             noise-reduction strengths the R, G and B channels
318             },
319             },
320             0x83bb => { # PH Extension!!
321             Name => 'IPTC-NAA', # (writable directory!)
322             Format => 'undef', # convert binary values as undef
323             Writable => 'int32u', # but write int32u format code in IFD
324             WriteGroup => 'IFD0',
325             Flags => [ 'Binary', 'Protected' ],
326             SubDirectory => {
327             DirName => 'IPTC',
328             TagTable => 'Image::ExifTool::IPTC::Main',
329             },
330             },
331             0x8769 => {
332             Name => 'ExifOffset',
333             Groups => { 1 => 'ExifIFD' },
334             Flags => 'SubIFD',
335             SubDirectory => {
336             TagTable => 'Image::ExifTool::Exif::Main',
337             DirName => 'ExifIFD',
338             Start => '$val',
339             },
340             },
341             0x8825 => {
342             Name => 'GPSInfo',
343             Groups => { 1 => 'GPS' },
344             Flags => 'SubIFD',
345             SubDirectory => {
346             DirName => 'GPS',
347             TagTable => 'Image::ExifTool::GPS::Main',
348             Start => '$val',
349             },
350             },
351             # 0xffff => 'DCSHueShiftValues', #exifprobe (NC)
352             );
353              
354             # white balance information (ref IB)
355             # (PanasonicRawVersion<200: Digilux 2)
356             %Image::ExifTool::PanasonicRaw::WBInfo = (
357             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
358             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
359             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
360             WRITABLE => 1,
361             FORMAT => 'int16u',
362             FIRST_ENTRY => 0,
363             0 => 'NumWBEntries',
364             1 => { Name => 'WBType1', %wbTypeInfo },
365             2 => { Name => 'WB_RBLevels1', Format => 'int16u[2]' },
366             4 => { Name => 'WBType2', %wbTypeInfo },
367             5 => { Name => 'WB_RBLevels2', Format => 'int16u[2]' },
368             7 => { Name => 'WBType3', %wbTypeInfo },
369             8 => { Name => 'WB_RBLevels3', Format => 'int16u[2]' },
370             10 => { Name => 'WBType4', %wbTypeInfo },
371             11 => { Name => 'WB_RBLevels4', Format => 'int16u[2]' },
372             13 => { Name => 'WBType5', %wbTypeInfo },
373             14 => { Name => 'WB_RBLevels5', Format => 'int16u[2]' },
374             16 => { Name => 'WBType6', %wbTypeInfo },
375             17 => { Name => 'WB_RBLevels6', Format => 'int16u[2]' },
376             19 => { Name => 'WBType7', %wbTypeInfo },
377             20 => { Name => 'WB_RBLevels7', Format => 'int16u[2]' },
378             );
379              
380             # white balance information (ref IB)
381             # (PanasonicRawVersion>=200: D-Lux2, D-Lux3, DMC-FZ18/FZ30/LX1/L10)
382             %Image::ExifTool::PanasonicRaw::WBInfo2 = (
383             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
384             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
385             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
386             WRITABLE => 1,
387             FORMAT => 'int16u',
388             FIRST_ENTRY => 0,
389             0 => 'NumWBEntries',
390             1 => { Name => 'WBType1', %wbTypeInfo },
391             2 => { Name => 'WB_RGBLevels1', Format => 'int16u[3]' },
392             5 => { Name => 'WBType2', %wbTypeInfo },
393             6 => { Name => 'WB_RGBLevels2', Format => 'int16u[3]' },
394             9 => { Name => 'WBType3', %wbTypeInfo },
395             10 => { Name => 'WB_RGBLevels3', Format => 'int16u[3]' },
396             13 => { Name => 'WBType4', %wbTypeInfo },
397             14 => { Name => 'WB_RGBLevels4', Format => 'int16u[3]' },
398             17 => { Name => 'WBType5', %wbTypeInfo },
399             18 => { Name => 'WB_RGBLevels5', Format => 'int16u[3]' },
400             21 => { Name => 'WBType6', %wbTypeInfo },
401             22 => { Name => 'WB_RGBLevels6', Format => 'int16u[3]' },
402             25 => { Name => 'WBType7', %wbTypeInfo },
403             26 => { Name => 'WB_RGBLevels7', Format => 'int16u[3]' },
404             );
405              
406             # lens distortion information (ref 3)
407             # (distortion correction equation: Ru = scale*(Rd + a*Rd^3 + b*Rd^5 + c*Rd^7), ref 3)
408             %Image::ExifTool::PanasonicRaw::DistortionInfo = (
409             PROCESS_PROC => \&ProcessDistortionInfo,
410             WRITE_PROC => \&WriteDistortionInfo,
411             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
412             # (don't make this family 0 MakerNotes because we don't want it to be a deletable group)
413             GROUPS => { 0 => 'PanasonicRaw', 1 => 'PanasonicRaw', 2 => 'Image'},
414             WRITABLE => 1,
415             FORMAT => 'int16s',
416             FIRST_ENTRY => 0,
417             NOTES => 'Lens distortion correction information.',
418             # 0,1 - checksums
419             2 => {
420             Name => 'DistortionParam02',
421             ValueConv => '$val / 32768',
422             ValueConvInv => '$val * 32768',
423             },
424             # 3 - usually 0, but seen 0x026b when value 5 is non-zero
425             4 => {
426             Name => 'DistortionParam04',
427             ValueConv => '$val / 32768',
428             ValueConvInv => '$val * 32768',
429             },
430             5 => {
431             Name => 'DistortionScale',
432             ValueConv => '1 / (1 + $val/32768)',
433             ValueConvInv => '(1/$val - 1) * 32768',
434             },
435             # 6 - seen 0x0000-0x027f
436             7.1 => {
437             Name => 'DistortionCorrection',
438             Mask => 0x0f,
439             # (have seen the upper 4 bits set for GF5 and GX1, giving a value of -4095 - PH)
440             PrintConv => { 0 => 'Off', 1 => 'On' },
441             },
442             8 => {
443             Name => 'DistortionParam08',
444             ValueConv => '$val / 32768',
445             ValueConvInv => '$val * 32768',
446             },
447             9 => {
448             Name => 'DistortionParam09',
449             ValueConv => '$val / 32768',
450             ValueConvInv => '$val * 32768',
451             },
452             # 10 - seen 0xfc,0x0101,0x01f4,0x021d,0x0256
453             11 => {
454             Name => 'DistortionParam11',
455             ValueConv => '$val / 32768',
456             ValueConvInv => '$val * 32768',
457             },
458             12 => {
459             Name => 'DistortionN',
460             Unknown => 1,
461             },
462             # 13 - seen 0x0000,0x01f9-0x02b2
463             # 14,15 - checksums
464             );
465              
466             # Panasonic RW2 camera IFD written by GH5 (ref PH)
467             # (doesn't seem to be valid for the GF7 or GM5 -- encrypted?)
468             %Image::ExifTool::PanasonicRaw::CameraIFD = (
469             GROUPS => { 0 => 'PanasonicRaw', 1 => 'CameraIFD', 2 => 'Camera'},
470             # (don't know what format codes 0x101 and 0x102 are for, so just
471             # map them into 4 = int32u for now)
472             VARS => { MAP_FORMAT => { 0x101 => 4, 0x102 => 4 } },
473             0x1001 => { #forum9388
474             Name => 'MultishotOn',
475             Writable => 'int32u',
476             PrintConv => { 0 => 'No', 1 => 'Yes' },
477             },
478             0x1100 => { #forum9274
479             Name => 'FocusStepNear',
480             Writable => 'int16s',
481             },
482             0x1101 => { #forum9274 (was forum8484)
483             Name => 'FocusStepCount',
484             Writable => 'int16s',
485             },
486             0x1102 => { #forum9417
487             Name => 'FlashFired',
488             Writable => 'int32u',
489             PrintConv => { 0 => 'No', 1 => 'Yes' },
490             },
491             # 0x1104 - set when camera shoots on lowest possible Extended-ISO (forum9290)
492             0x1105 => { #forum9392
493             Name => 'ZoomPosition',
494             Notes => 'in the range 0-255 for most cameras',
495             Writable => 'int32u',
496             },
497             0x1200 => { #forum9278
498             Name => 'LensAttached',
499             Notes => 'many CameraIFD tags are invalid if there is no lens attached',
500             Writable => 'int32u',
501             PrintConv => { 0 => 'No', 1 => 'Yes' },
502             },
503             # Note: LensTypeMake and LensTypeModel are combined into a Composite LensType tag
504             # defined in Olympus.pm which has the same values as Olympus:LensType
505             0x1201 => { #IB
506             Name => 'LensTypeMake',
507             Condition => '$format eq "int16u"',
508             Writable => 'int16u',
509             # when format is int16u, these values have been observed:
510             # 0 - Olympus or unknown lens
511             # 2 - Leica or Lumix lens
512             # when format is int32u (S models), these values have been observed (ref IB):
513             # 256 - Leica lens
514             # 257 - Lumix lens
515             # 258 - ? (seen once)
516             },
517             0x1202 => { #IB
518             Name => 'LensTypeModel',
519             Condition => '$format eq "int16u"',
520             Writable => 'int16u',
521             RawConv => q{
522             return undef unless $val;
523             require Image::ExifTool::Olympus; # (to load Composite LensID)
524             return $val;
525             },
526             ValueConv => '$_=sprintf("%.4x",$val); s/(..)(..)/$2 $1/; $_',
527             ValueConvInv => '$val =~ s/(..) (..)/$2$1/; hex($val)',
528             },
529             0x1203 => { #4
530             Name => 'FocalLengthIn35mmFormat',
531             Writable => 'int16u',
532             PrintConv => '"$val mm"',
533             PrintConvInv => '$val=~s/\s*mm$//;$val',
534             },
535             # 0x1300 - incident light value? (ref forum11395)
536             0x1301 => { #forum11395
537             Name => 'ApertureValue',
538             Writable => 'int16s',
539             Priority => 0,
540             ValueConv => '2 ** ($val / 512)',
541             ValueConvInv => '$val>0 ? 512*log($val)/log(2) : 0',
542             PrintConv => 'sprintf("%.1f",$val)',
543             PrintConvInv => '$val',
544             },
545             0x1302 => { #forum11395
546             Name => 'ShutterSpeedValue',
547             Writable => 'int16s',
548             Priority => 0,
549             ValueConv => 'abs($val/256)<100 ? 2**(-$val/256) : 0',
550             ValueConvInv => '$val>0 ? -256*log($val)/log(2) : -25600',
551             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
552             PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
553             },
554             0x1303 => { #forum11395
555             Name => 'SensitivityValue',
556             Writable => 'int16s',
557             ValueConv => '$val / 256',
558             ValueConvInv => 'int($val * 256)',
559             },
560             0x1305 => { #forum9384
561             Name => 'HighISOMode',
562             Writable => 'int16u',
563             RawConv => '$val || undef',
564             PrintConv => { 1 => 'On', 2 => 'Off' },
565             },
566             # 0x1306 EV for some models like the GX8 (forum11395)
567             # 0x140b - scaled overall black level? (ref forum9281)
568             # 0x1411 - scaled black level per channel difference (ref forum9281)
569             0x1412 => { #forum11397
570             Name => 'FacesDetected',
571             Writable => 'int8u',
572             PrintConv => { 0 => 'No', 1 => 'Yes' },
573             },
574             # 0x2000 - WB tungsten=3, daylight=4 (ref forum9467)
575             # 0x2009 - scaled black level per channel (ref forum9281)
576             # 0x3000-0x310b - red/blue balances * 1024 (ref forum9467)
577             # 0x3000 modifiedTungsten-Red (-2?)
578             # 0x3001 modifiedTungsten-Blue (-2?)
579             # 0x3002 modifiedDaylight-Red (-2?)
580             # 0x3003 modifiedDaylight-Blue (-2?)
581             # 0x3004 modifiedTungsten-Red (-1?)
582             # 0x3005 modifiedTungsten-Blue (-1?)
583             # 0x3006 modifiedDaylight-Red (-1?)
584             # 0x3007 modifiedDaylight-Blue (-1?)
585             # 0x3100 DefaultTungsten-Red
586             # 0x3101 DefaultTungsten-Blue
587             # 0x3102 DefaultDaylight-Red
588             # 0x3103 DefaultDaylight-Blue
589             # 0x3104 modifiedTungsten-Red (+1?)
590             # 0x3105 modifiedTungsten-Blue (+1?)
591             # 0x3106 modifiedDaylight-Red (+1?)
592             # 0x3107 modifiedDaylight-Blue (+1?)
593             # 0x3108 modifiedTungsten-Red (+2?)
594             # 0x3109 modifiedTungsten-Blue (+2?)
595             # 0x310a modifiedDaylight-Red (+2?)
596             # 0x310b modifiedDaylight-Blue (+2?)
597             0x3200 => { #forum9275
598             Name => 'WB_CFA0_LevelDaylight',
599             Writable => 'int16u',
600             },
601             0x3201 => { #forum9275
602             Name => 'WB_CFA1_LevelDaylight',
603             Writable => 'int16u',
604             },
605             0x3202 => { #forum9275
606             Name => 'WB_CFA2_LevelDaylight',
607             Writable => 'int16u',
608             },
609             0x3203 => { #forum9275
610             Name => 'WB_CFA3_LevelDaylight',
611             Writable => 'int16u',
612             },
613             # 0x3204-0x3207 - user multipliers * 1024 ? (ref forum9275)
614             # 0x320a - scaled maximum value of raw data (scaling = 4x) (ref forum9281)
615             # 0x3209 - gamma (x256) (ref forum9281)
616             0x3300 => { #forum9296/9396
617             Name => 'WhiteBalanceSet',
618             Writable => 'int8u',
619             PrintConv => \%panasonicWhiteBalance,
620             SeparateTable => 'WhiteBalance',
621             },
622             0x3420 => { #forum9276
623             Name => 'WB_RedLevelAuto',
624             Writable => 'int16u',
625             },
626             0x3421 => { #forum9276
627             Name => 'WB_BlueLevelAuto',
628             Writable => 'int16u',
629             },
630             0x3501 => { #4
631             Name => 'Orientation',
632             Writable => 'int8u',
633             PrintConv => \%Image::ExifTool::Exif::orientation,
634             },
635             # 0x3504 = Tag 0x1301+0x1302-0x1303 (Bv = Av+Tv-Sv) (forum11395)
636             # 0x3505 - same as 0x1300 (forum11395)
637             0x3600 => { #forum9396
638             Name => 'WhiteBalanceDetected',
639             Writable => 'int8u',
640             PrintConv => \%panasonicWhiteBalance,
641             SeparateTable => 'WhiteBalance',
642             },
643             );
644              
645             # PanasonicRaw composite tags
646             %Image::ExifTool::PanasonicRaw::Composite = (
647             ImageWidth => {
648             Require => {
649             0 => 'IFD0:SensorLeftBorder',
650             1 => 'IFD0:SensorRightBorder',
651             },
652             ValueConv => '$val[1] - $val[0]',
653             },
654             ImageHeight => {
655             Require => {
656             0 => 'IFD0:SensorTopBorder',
657             1 => 'IFD0:SensorBottomBorder',
658             },
659             ValueConv => '$val[1] - $val[0]',
660             },
661             );
662              
663             # add our composite tags
664             Image::ExifTool::AddCompositeTags('Image::ExifTool::PanasonicRaw');
665              
666              
667             #------------------------------------------------------------------------------
668             # checksum algorithm for lens distortion correction information (ref 3)
669             # Inputs: 0) data ref, 1) start position, 2) number of bytes, 3) incement
670             # Returns: checksum value
671             sub Checksum($$$$)
672             {
673 12     12 0 22 my ($dataPt, $start, $num, $inc) = @_;
674 12         17 my $csum = 0;
675 12         16 my $i;
676 12         29 for ($i=0; $i<$num; ++$i) {
677 156         271 $csum = (73 * $csum + Get8u($dataPt, $start + $i * $inc)) % 0xffef;
678             }
679 12         45 return $csum;
680             }
681              
682             #------------------------------------------------------------------------------
683             # Read lens distortion information
684             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
685             # Returns: 1 on success
686             sub ProcessDistortionInfo($$$)
687             {
688 2     2 0 7 my ($et, $dirInfo, $tagTablePtr) = @_;
689 2         7 my $dataPt = $$dirInfo{DataPt};
690 2   50     11 my $start = $$dirInfo{DirStart} || 0;
691 2   33     13 my $size = $$dirInfo{DirLen} || (length($$dataPt) - $start);
692 2 50       6 if ($size == 32) {
693             # verify the checksums (ref 3)
694 2         10 my $csum1 = Checksum($dataPt, $start + 4, 12, 1);
695 2         7 my $csum2 = Checksum($dataPt, $start + 16, 12, 1);
696 2         6 my $csum3 = Checksum($dataPt, $start + 2, 14, 2);
697 2         7 my $csum4 = Checksum($dataPt, $start + 3, 14, 2);
698 2         8 my $res = $csum1 ^ Get16u($dataPt, $start + 2) ^
699             $csum2 ^ Get16u($dataPt, $start + 28) ^
700             $csum3 ^ Get16u($dataPt, $start + 0) ^
701             $csum4 ^ Get16u($dataPt, $start + 30);
702 2 50       9 $et->Warn('Invalid DistortionInfo checksum',1) if $res;
703             } else {
704 0         0 $et->Warn('Invalid DistortionInfo',1);
705             }
706 2         13 return $et->ProcessBinaryData($dirInfo, $tagTablePtr);
707             }
708              
709             #------------------------------------------------------------------------------
710             # Write lens distortion information
711             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
712             # Returns: updated distortion information or undef on error
713             sub WriteDistortionInfo($$$)
714             {
715 9     9 0 18 my ($et, $dirInfo, $tagTablePtr) = @_;
716 9 100       34 $et or return 1; # (allow dummy access)
717 1         7 my $dat = $et->WriteBinaryData($dirInfo, $tagTablePtr);
718 1 50 33     9 if (defined $dat and length($dat) == 32) {
719             # fix checksums (ref 3)
720 1         6 Set16u(Checksum(\$dat, 4, 12, 1), \$dat, 2);
721 1         5 Set16u(Checksum(\$dat, 16, 12, 1), \$dat, 28);
722 1         3 Set16u(Checksum(\$dat, 2, 14, 2), \$dat, 0);
723 1         4 Set16u(Checksum(\$dat, 3, 14, 2), \$dat, 30);
724             } else {
725 0         0 $et->Warn('Error wriing DistortionInfo',1);
726             }
727 1         4 return $dat;
728             }
729              
730             #------------------------------------------------------------------------------
731             # Patch for writing non-standard Panasonic RAW/RW2/RWL raw data
732             # Inputs: 0) offset info ref, 1) raf ref, 2) IFD number
733             # Returns: error string, or undef on success
734             # OffsetInfo is a hash by tag ID of lists with the following elements:
735             # 0 - tag info ref
736             # 1 - pointer to int32u offset in IFD or value data
737             # 2 - value count
738             # 3 - reference to list of original offset values
739             # 4 - IFD format number
740             sub PatchRawDataOffset($$$)
741             {
742 1     1 0 5 my ($offsetInfo, $raf, $ifd) = @_;
743 1         4 my $stripOffsets = $$offsetInfo{0x111};
744 1         3 my $stripByteCounts = $$offsetInfo{0x117};
745 1         3 my $rawDataOffset = $$offsetInfo{0x118};
746 1         3 my $err;
747 1 50       5 $err = 1 unless $ifd == 0;
748 1 50 33     11 $err = 1 unless $stripOffsets and $stripByteCounts and $$stripOffsets[2] == 1;
      33        
749 1 50       3 if ($rawDataOffset) {
750 1 50       5 $err = 1 unless $$rawDataOffset[2] == 1;
751 1 50 33     6 $err = 1 unless $$stripOffsets[3][0] == 0xffffffff or $$stripByteCounts[3][0] == 0;
752             }
753 1 50       3 $err and return 'Unsupported Panasonic/Leica RAW variant';
754 1 50       4 if ($rawDataOffset) {
755             # update StripOffsets along with this tag if it contains a reasonable value
756 1 50       7 unless ($$stripOffsets[3][0] == 0xffffffff) {
757             # save pointer to StripOffsets value for updating later
758 0         0 push @$rawDataOffset, $$stripOffsets[1];
759             }
760             # handle via RawDataOffset instead of StripOffsets
761 1         4 $stripOffsets = $$offsetInfo{0x111} = $rawDataOffset;
762 1         3 delete $$offsetInfo{0x118};
763             }
764             # determine the length of the raw data
765 1         7 my $pos = $raf->Tell();
766 1 50       6 $raf->Seek(0, 2) or $err = 1; # seek to end of file
767 1         5 my $len = $raf->Tell() - $$stripOffsets[3][0];
768 1         4 $raf->Seek($pos, 0);
769             # quick check to be sure the raw data length isn't unreasonable
770             # (the 22-byte length is for '' in our tests)
771 1 50 33     13 $err = 1 if ($len < 1000 and $len != 22) or $len & 0x80000000;
      33        
772 1 50       4 $err and return 'Error reading Panasonic raw data';
773             # update StripByteCounts info with raw data length
774             # (note that the original value is maintained in the file)
775 1         3 $$stripByteCounts[3][0] = $len;
776              
777 1         4 return undef;
778             }
779              
780             #------------------------------------------------------------------------------
781             # Write meta information to Panasonic JpgFromRaw in RAW/RW2/RWL image
782             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
783             # Returns: updated image data, or undef if nothing changed
784             sub WriteJpgFromRaw($$$)
785             {
786 1     1 0 4 my ($et, $dirInfo, $tagTablePtr) = @_;
787 1         4 my $dataPt = $$dirInfo{DataPt};
788 1         17 my $byteOrder = GetByteOrder();
789 1         6 my $fileType = $$et{TIFF_TYPE}; # RAW, RW2 or RWL
790 1         2 my $dirStart = $$dirInfo{DirStart};
791 1 50       5 if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL
792 0         0 my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart;
793 0         0 my $buff = substr($$dataPt, $dirStart, $dirLen);
794 0         0 $dataPt = \$buff;
795             }
796 1         7 my $raf = new File::RandomAccess($dataPt);
797 1         2 my $outbuff;
798 1         4 my %dirInfo = (
799             RAF => $raf,
800             OutFile => \$outbuff,
801             );
802 1         4 $$et{BASE} = $$dirInfo{DataPos};
803 1         4 $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG';
804             # use a specialized map so we don't write XMP or IPTC (or other junk) into the JPEG
805 1         3 my $editDirs = $$et{EDIT_DIRS};
806 1         3 my $addDirs = $$et{ADD_DIRS};
807 1         6 $et->InitWriteDirs(\%jpgFromRawMap);
808             # don't add XMP segment (IPTC won't get added because it is in Photoshop record)
809 1         3 delete $$et{ADD_DIRS}{XMP};
810 1         6 my $result = $et->WriteJPEG(\%dirInfo);
811             # restore variables we changed
812 1         5 $$et{BASE} = 0;
813 1         4 $$et{FILE_TYPE} = 'TIFF';
814 1         5 $$et{TIFF_TYPE} = $fileType;
815 1         7 $$et{EDIT_DIRS} = $editDirs;
816 1         4 $$et{ADD_DIRS} = $addDirs;
817 1         7 SetByteOrder($byteOrder);
818 1 50       13 return $result > 0 ? $outbuff : $$dataPt;
819             }
820              
821             #------------------------------------------------------------------------------
822             # Extract meta information from an Panasonic JpgFromRaw
823             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
824             # Returns: 1 on success, 0 if this wasn't a valid JpgFromRaw image
825             sub ProcessJpgFromRaw($$$)
826             {
827 2     2 0 8 my ($et, $dirInfo, $tagTablePtr) = @_;
828 2         9 my $dataPt = $$dirInfo{DataPt};
829 2         7 my $byteOrder = GetByteOrder();
830 2         5 my $fileType = $$et{TIFF_TYPE}; # RAW, RW2 or RWL
831 2         6 my $tagInfo = $$dirInfo{TagInfo};
832 2         11 my $verbose = $et->Options('Verbose');
833 2         4 my ($indent, $out);
834 2 50       9 $tagInfo or $et->Warn('No tag info for Panasonic JpgFromRaw'), return 0;
835 2         7 my $dirStart = $$dirInfo{DirStart};
836 2 50       7 if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL
837 0         0 my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart;
838 0         0 my $buff = substr($$dataPt, $dirStart, $dirLen);
839 0         0 $dataPt = \$buff;
840             }
841 2   50     15 $$et{BASE} = $$dirInfo{DataPos} + ($dirStart || 0);
842 2         25 $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG';
843 2         7 $$et{DOC_NUM} = 1;
844             # extract information from embedded JPEG
845 2         22 my %dirInfo = (
846             Parent => 'RAF',
847             RAF => new File::RandomAccess($dataPt),
848             );
849 2 50       8 if ($verbose) {
850 0         0 my $indent = $$et{INDENT};
851 0         0 $$et{INDENT} = ' ';
852 0         0 $out = $et->Options('TextOut');
853 0         0 print $out '--- DOC1:JpgFromRaw ',('-'x56),"\n";
854             }
855             # fudge HtmlDump base offsets to show as a stand-alone JPEG
856 2         7 $$et{BASE_FUDGE} = $$et{BASE};
857 2         12 my $rtnVal = $et->ProcessJPEG(\%dirInfo);
858 2         6 $$et{BASE_FUDGE} = 0;
859             # restore necessary variables for continued RW2/RWL processing
860 2         8 $$et{BASE} = 0;
861 2         7 $$et{FILE_TYPE} = 'TIFF';
862 2         6 $$et{TIFF_TYPE} = $fileType;
863 2         8 delete $$et{DOC_NUM};
864 2         11 SetByteOrder($byteOrder);
865 2 50       6 if ($verbose) {
866 0         0 $$et{INDENT} = $indent;
867 0         0 print $out ('-'x76),"\n";
868             }
869 2         15 return $rtnVal;
870             }
871              
872             1; # end
873              
874             __END__