SigmaRaw.pm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. #------------------------------------------------------------------------------
  2. # File: SigmaRaw.pm
  3. #
  4. # Description: Read Sigma/Foveon RAW (X3F) meta information
  5. #
  6. # Revisions: 2005/10/16 - P. Harvey Created
  7. # 2009/11/30 - P. Harvey Support X3F v2.3 written by Sigma DP2
  8. #
  9. # References: 1) http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf
  10. #------------------------------------------------------------------------------
  11. package Image::ExifTool::SigmaRaw;
  12. use strict;
  13. use vars qw($VERSION);
  14. use Image::ExifTool qw(:DataAccess :Utils);
  15. use Image::ExifTool::Sigma;
  16. $VERSION = '1.24';
  17. sub ProcessX3FHeader($$$);
  18. sub ProcessX3FDirectory($$$);
  19. sub ProcessX3FProperties($$$);
  20. # main X3F sections (plus header stuff)
  21. %Image::ExifTool::SigmaRaw::Main = (
  22. PROCESS_PROC => \&ProcessX3FDirectory,
  23. NOTES => q{
  24. These tags are used in Sigma and Foveon RAW (.X3F) images. Metadata is also
  25. extracted from the JpgFromRaw image if it exists (all models but the SD9 and
  26. SD10). Currently, metadata may only be written to the embedded JpgFromRaw.
  27. },
  28. Header => {
  29. SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header' },
  30. },
  31. Header4 => {
  32. SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header4' },
  33. },
  34. HeaderExt => {
  35. SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::HeaderExt' },
  36. },
  37. PROP => {
  38. Name => 'Properties',
  39. SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Properties' },
  40. },
  41. IMAG => {
  42. Name => 'PreviewImage',
  43. Groups => { 2 => 'Preview' },
  44. Binary => 1,
  45. },
  46. IMA2 => [
  47. {
  48. Name => 'PreviewImage',
  49. Condition => 'not $$self{IsJpgFromRaw}',
  50. Groups => { 2 => 'Preview' },
  51. Binary => 1,
  52. },
  53. {
  54. Name => 'JpgFromRaw',
  55. Groups => { 2 => 'Preview' },
  56. Binary => 1,
  57. },
  58. ]
  59. );
  60. # common X3F header structure
  61. %Image::ExifTool::SigmaRaw::Header = (
  62. PROCESS_PROC => \&ProcessX3FHeader,
  63. FORMAT => 'int32u',
  64. NOTES => 'Information extracted from the header of an X3F file.',
  65. 1 => {
  66. Name => 'FileVersion',
  67. ValueConv => '($val >> 16) . "." . ($val & 0xffff)',
  68. },
  69. 2 => {
  70. Name => 'ImageUniqueID',
  71. # the serial number (with an extra leading "0") makes up
  72. # the first 8 digits of this UID,
  73. Format => 'undef[16]',
  74. ValueConv => 'unpack("H*", $val)',
  75. },
  76. 6 => {
  77. Name => 'MarkBits',
  78. PrintConv => { BITMASK => { } },
  79. },
  80. 7 => 'ImageWidth',
  81. 8 => 'ImageHeight',
  82. 9 => 'Rotation',
  83. 10 => {
  84. Name => 'WhiteBalance',
  85. Format => 'string[32]',
  86. },
  87. 18 => { #PH (DP2, FileVersion 2.3)
  88. Name => 'SceneCaptureType',
  89. Format => 'string[32]',
  90. },
  91. );
  92. # X3F version 4 header structure (ref PH)
  93. %Image::ExifTool::SigmaRaw::Header4 = (
  94. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  95. FORMAT => 'int32u',
  96. NOTES => 'Header information for version 4.0 or greater X3F.',
  97. 1 => {
  98. Name => 'FileVersion',
  99. ValueConv => '($val >> 16) . "." . ($val & 0xffff)',
  100. },
  101. # 8 - undef[4]: 4 random ASCII characters
  102. 10 => 'ImageWidth',
  103. 11 => 'ImageHeight',
  104. 12 => 'Rotation',
  105. # don't know what the rest of the header contains, but none of
  106. # these values change in any of my samples...
  107. );
  108. # extended header tags
  109. %Image::ExifTool::SigmaRaw::HeaderExt = (
  110. GROUPS => { 2 => 'Camera' },
  111. FORMAT => 'float',
  112. NOTES => 'Extended header data found in version 2.1 and 2.2 files',
  113. 0 => 'Unused',
  114. 1 => { Name => 'ExposureAdjust',PrintConv => 'sprintf("%.1f",$val)' },
  115. 2 => { Name => 'Contrast', PrintConv => 'sprintf("%.1f",$val)' },
  116. 3 => { Name => 'Shadow', PrintConv => 'sprintf("%.1f",$val)' },
  117. 4 => { Name => 'Highlight', PrintConv => 'sprintf("%.1f",$val)' },
  118. 5 => { Name => 'Saturation', PrintConv => 'sprintf("%.1f",$val)' },
  119. 6 => { Name => 'Sharpness', PrintConv => 'sprintf("%.1f",$val)' },
  120. 7 => { Name => 'RedAdjust', PrintConv => 'sprintf("%.1f",$val)' },
  121. 8 => { Name => 'GreenAdjust', PrintConv => 'sprintf("%.1f",$val)' },
  122. 9 => { Name => 'BlueAdjust', PrintConv => 'sprintf("%.1f",$val)' },
  123. 10 => { Name => 'X3FillLight', PrintConv => 'sprintf("%.1f",$val)' },
  124. );
  125. # PROP tags
  126. %Image::ExifTool::SigmaRaw::Properties = (
  127. PROCESS_PROC => \&ProcessX3FProperties,
  128. GROUPS => { 2 => 'Camera' },
  129. PRIORITY => 0, # (because these aren't writable like the EXIF ones)
  130. AEMODE => {
  131. Name => 'MeteringMode',
  132. PrintConv => {
  133. 8 => '8-segment',
  134. C => 'Center-weighted average',
  135. A => 'Average',
  136. },
  137. },
  138. AFAREA => 'AFArea', # observed: CENTER_V
  139. AFINFOCUS => 'AFInFocus', # observed: H
  140. AFMODE => 'FocusMode',
  141. AP_DESC => 'ApertureDisplayed',
  142. APERTURE => {
  143. Name => 'FNumber',
  144. Groups => { 2 => 'Image' },
  145. PrintConv => 'sprintf("%.1f",$val)',
  146. },
  147. BRACKET => 'BracketShot',
  148. BURST => 'BurstShot',
  149. CAMMANUF => 'Make',
  150. CAMMODEL => 'Model',
  151. CAMNAME => 'CameraName',
  152. CAMSERIAL => 'SerialNumber',
  153. CM_DESC => 'SceneCaptureType', #PH (DP2)
  154. COLORSPACE => 'ColorSpace', # observed: sRGB
  155. DRIVE => {
  156. Name => 'DriveMode',
  157. PrintConv => {
  158. SINGLE => 'Single Shot',
  159. MULTI => 'Multi Shot',
  160. '2S' => '2 s Timer',
  161. '10S' => '10 s Timer',
  162. UP => 'Mirror Up',
  163. AB => 'Auto Bracket',
  164. OFF => 'Off',
  165. },
  166. },
  167. EVAL_STATE => 'EvalState', # observed: POST-EXPOSURE
  168. EXPCOMP => {
  169. Name => 'ExposureCompensation',
  170. Groups => { 2 => 'Image' },
  171. PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
  172. },
  173. EXPNET => {
  174. Name => 'NetExposureCompensation',
  175. Groups => { 2 => 'Image' },
  176. PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
  177. },
  178. EXPTIME => {
  179. Name => 'IntegrationTime',
  180. Groups => { 2 => 'Image' },
  181. ValueConv => '$val * 1e-6', # convert from usec
  182. PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
  183. },
  184. FIRMVERS => 'FirmwareVersion',
  185. FLASH => {
  186. Name => 'FlashMode',
  187. PrintConv => 'ucfirst(lc($val))',
  188. },
  189. FLASHEXPCOMP=> 'FlashExpComp',
  190. FLASHPOWER => 'FlashPower',
  191. FLASHTTLMODE=> 'FlashTTLMode', # observed: ON
  192. FLASHTYPE => 'FlashType', # observed: NONE
  193. FLENGTH => {
  194. Name => 'FocalLength',
  195. PrintConv => 'sprintf("%.1f mm",$val)',
  196. },
  197. FLEQ35MM => {
  198. Name => 'FocalLengthIn35mmFormat',
  199. PrintConv => 'sprintf("%.1f mm",$val)',
  200. },
  201. FOCUS => {
  202. Name => 'Focus',
  203. PrintConv => {
  204. AF => 'Auto-focus Locked',
  205. 'NO LOCK' => "Auto-focus Didn't Lock",
  206. M => 'Manual',
  207. },
  208. },
  209. IMAGERBOARDID => 'ImagerBoardID',
  210. IMAGERTEMP => {
  211. Name => 'SensorTemperature',
  212. PrintConv => '"$val C"',
  213. },
  214. IMAGEBOARDID=> 'ImageBoardID', #PH (DP2)
  215. ISO => 'ISO',
  216. LENSARANGE => 'LensApertureRange',
  217. LENSFRANGE => 'LensFocalRange',
  218. LENSMODEL => {
  219. Name => 'LensType',
  220. ValueConvInv => '$val=~s/\.\d+$//; $val', # (truncate decimal part)
  221. PrintConv => \%Image::ExifTool::Sigma::sigmaLensTypes,
  222. SeparateTable => 'Sigma LensType',
  223. },
  224. PMODE => {
  225. Name => 'ExposureProgram',
  226. PrintConv => {
  227. P => 'Program',
  228. A => 'Aperture Priority',
  229. S => 'Shutter Priority',
  230. M => 'Manual',
  231. },
  232. },
  233. RESOLUTION => {
  234. Name => 'Quality',
  235. PrintConv => {
  236. LOW => 'Low',
  237. MED => 'Medium',
  238. HI => 'High',
  239. },
  240. },
  241. SENSORID => 'SensorID',
  242. SH_DESC => 'ShutterSpeedDisplayed',
  243. SHUTTER => {
  244. Name => 'ExposureTime',
  245. Groups => { 2 => 'Image' },
  246. PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
  247. },
  248. TIME => {
  249. Name => 'DateTimeOriginal',
  250. Groups => { 2 => 'Time' },
  251. Description => 'Date/Time Original',
  252. ValueConv => 'ConvertUnixTime($val)',
  253. PrintConv => '$self->ConvertDateTime($val)',
  254. },
  255. WB_DESC => 'WhiteBalance',
  256. VERSION_BF => 'VersionBF',
  257. );
  258. #------------------------------------------------------------------------------
  259. # Extract null-terminated unicode string from list of characters
  260. # Inputs: 0) ExifTool ref, 1) list ref, 2) position in list
  261. # Returns: Converted string
  262. sub ExtractUnicodeString($$$)
  263. {
  264. my ($et, $chars, $pos) = @_;
  265. my $i;
  266. for ($i=$pos; $i<@$chars; ++$i) {
  267. last unless $$chars[$i];
  268. }
  269. my $buff = pack('v*', @$chars[$pos..$i-1]);
  270. return $et->Decode($buff, 'UCS2', 'II');
  271. }
  272. #------------------------------------------------------------------------------
  273. # Process an X3F header
  274. # Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
  275. # Returns: 1 on success
  276. sub ProcessX3FHeader($$$)
  277. {
  278. my ($et, $dirInfo, $tagTablePtr) = @_;
  279. my $dataPt = $$dirInfo{DataPt};
  280. my $hdrLen = $$dirInfo{DirLen};
  281. # process the static header structure first
  282. $et->ProcessBinaryData($dirInfo, $tagTablePtr);
  283. # process extended data if available
  284. if (length($$dataPt) - $hdrLen >= 160) {
  285. my $verbose = $et->Options('Verbose');
  286. if ($verbose) {
  287. $et->VerboseDir('X3F HeaderExt', 32);
  288. HexDump($dataPt, undef,
  289. MaxLen => $verbose > 3 ? 1024 : 96,
  290. Out => $et->Options('TextOut'),
  291. Prefix => $$et{INDENT},
  292. Start => $hdrLen,
  293. ) if $verbose > 2;
  294. }
  295. $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::HeaderExt');
  296. my @tags = unpack("x${hdrLen}C32", $$dataPt);
  297. my $i;
  298. my $unused = 0;
  299. for ($i=0; $i<32; ++$i) {
  300. $tags[$i] or ++$unused, next;
  301. $et->HandleTag($tagTablePtr, $tags[$i], undef,
  302. Index => $i,
  303. DataPt => $dataPt,
  304. Start => $hdrLen + 32 + $i * 4,
  305. Size => 4,
  306. );
  307. }
  308. $et->VPrint(0, "$$et{INDENT}($unused entries unused)\n");
  309. }
  310. return 1;
  311. }
  312. #------------------------------------------------------------------------------
  313. # Process an X3F properties
  314. # Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
  315. # Returns: 1 on success
  316. sub ProcessX3FProperties($$$)
  317. {
  318. my ($et, $dirInfo, $tagTablePtr) = @_;
  319. my $dataPt = $$dirInfo{DataPt};
  320. my $size = length($$dataPt);
  321. my $verbose = $et->Options('Verbose');
  322. my $unknown = $et->Options('Unknown');
  323. unless ($size >= 24 and $$dataPt =~ /^SECp/) {
  324. $et->Warn('Bad properties header');
  325. return 0;
  326. }
  327. my ($entries, $fmt, $len) = unpack('x8V2x4V', $$dataPt);
  328. unless ($size >= 24 + 8 * $entries + $len) {
  329. $et->Warn('Truncated Property directory');
  330. return 0;
  331. }
  332. $verbose and $et->VerboseDir('Properties', $entries);
  333. $fmt == 0 or $et->Warn("Unsupported character format $fmt"), return 0;
  334. my $charPos = 24 + 8 * $entries;
  335. my @chars = unpack('v*',substr($$dataPt, $charPos, $len * 2));
  336. my $index;
  337. for ($index=0; $index<$entries; ++$index) {
  338. my ($namePos, $valPos) = unpack('V2',substr($$dataPt, $index*8 + 24, 8));
  339. if ($namePos >= @chars or $valPos >= @chars) {
  340. $et->Warn('Bad Property pointer');
  341. return 0;
  342. }
  343. my $tag = ExtractUnicodeString($et, \@chars, $namePos);
  344. my $val = ExtractUnicodeString($et, \@chars, $valPos);
  345. if (not $$tagTablePtr{$tag} and $unknown and $tag =~ /^\w+$/) {
  346. my $tagInfo = {
  347. Name => "SigmaRaw_$tag",
  348. Description => Image::ExifTool::MakeDescription('SigmaRaw', $tag),
  349. Unknown => 1,
  350. Writable => 0, # can't write unknown tags
  351. };
  352. # add tag information to table
  353. AddTagToTable($tagTablePtr, $tag, $tagInfo);
  354. }
  355. $et->HandleTag($tagTablePtr, $tag, $val,
  356. Index => $index,
  357. DataPt => $dataPt,
  358. Start => $charPos + 2 * $valPos,
  359. Size => 2 * (length($val) + 1),
  360. );
  361. }
  362. return 1;
  363. }
  364. #------------------------------------------------------------------------------
  365. # Write an X3F file
  366. # Inputs: 0) ExifTool ref, 1) DirInfo ref (DirStart = directory offset)
  367. # Returns: error string, undef on success, or -1 on write error
  368. # Notes: Writes metadata to embedded JpgFromRaw image
  369. sub WriteX3F($$)
  370. {
  371. my ($et, $dirInfo) = @_;
  372. my $raf = $$dirInfo{RAF};
  373. my $outfile = $$dirInfo{OutFile};
  374. my ($outDir, $buff, $ver, $entries, $dir, $outPos, $index, $didContain);
  375. $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start';
  376. # read the X3F directory header (will be copied directly to output)
  377. $raf->Read($outDir, 12) == 12 or return 'Truncated X3F image';
  378. $outDir =~ /^SECd/ or return 'Bad section header';
  379. ($ver, $entries) = unpack('x4V2', $outDir);
  380. # do sanity check on number of entries in directory
  381. return 'Invalid X3F directory count' unless $entries > 2 and $entries < 20;
  382. # read the directory entries
  383. unless ($raf->Read($dir, $entries * 12) == $entries * 12) {
  384. return 'Truncated X3F directory';
  385. }
  386. # do a quick scan to determine the offset of the first data subsection
  387. for ($index=0; $index<$entries; ++$index) {
  388. my $pos = $index * 12;
  389. my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
  390. # remember position of first data subsection
  391. $outPos = $offset if not defined $outPos or $outPos > $offset;
  392. }
  393. # copy the file header up to the start of the first data subsection
  394. unless ($raf->Seek(0,0) and $raf->Read($buff, $outPos) == $outPos) {
  395. return 'Error reading X3F header';
  396. }
  397. Write($outfile, $buff) or return -1;
  398. # loop through directory, rewriting each section
  399. for ($index=0; $index<$entries; ++$index) {
  400. my $pos = $index * 12;
  401. my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
  402. $raf->Seek($offset, 0) or return 'Bad data offset';
  403. if ($tag eq 'IMA2' and $len > 28) {
  404. # check subsection header (28 bytes) to see if this is a JPEG preview image
  405. $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
  406. Write($outfile, $buff) or return -1;
  407. $len -= 28;
  408. # only rewrite full-sized JpgFromRaw (version 2.0, type 2, format 18)
  409. if ($buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/ and
  410. $$et{ImageWidth} == unpack('x16V', $buff))
  411. {
  412. $raf->Read($buff, $len) == $len or return 'Error reading JpgFromRaw';
  413. # use same write directories as JPEG
  414. $et->InitWriteDirs('JPEG');
  415. # rewrite the embedded JPEG in memory
  416. my $newData;
  417. my %jpegInfo = (
  418. Parent => 'X3F',
  419. RAF => new File::RandomAccess(\$buff),
  420. OutFile => \$newData,
  421. );
  422. $$et{FILE_TYPE} = 'JPEG';
  423. my $success = $et->WriteJPEG(\%jpegInfo);
  424. $$et{FILE_TYPE} = 'X3F';
  425. SetByteOrder('II');
  426. return 'Error writing X3F JpgFromRaw' unless $success and $newData;
  427. return -1 if $success < 0;
  428. # write new data if anything changed, otherwise copy old image
  429. my $outPt = $$et{CHANGED} ? \$newData : \$buff;
  430. Write($outfile, $$outPt) or return -1;
  431. # set $len to the total subsection data length
  432. $len = length($$outPt) + 28;
  433. $didContain = 1;
  434. } else {
  435. # copy original image data
  436. Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F image';
  437. $len += 28;
  438. }
  439. } else {
  440. # copy data for this subsection
  441. Image::ExifTool::CopyBlock($raf, $outfile, $len) or return 'Corrupted X3F directory';
  442. }
  443. # add directory entry and update output file position
  444. $outDir .= pack('V2a4', $outPos, $len, $tag);
  445. $outPos += $len;
  446. # pad data to an even 4-byte boundary
  447. if ($len & 0x03) {
  448. my $pad = 4 - ($len & 0x03);
  449. Write($outfile, "\0" x $pad) or return -1;
  450. $outPos += $pad;
  451. }
  452. }
  453. # warn if we couldn't add metadata to this image (should only be SD9 or SD10)
  454. $didContain or $et->Warn("Can't yet write SD9 or SD10 X3F images");
  455. # write out the directory and the directory pointer, and we are done
  456. Write($outfile, $outDir, pack('V', $outPos)) or return -1;
  457. return undef;
  458. }
  459. #------------------------------------------------------------------------------
  460. # Process an X3F directory
  461. # Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
  462. # Returns: error string or undef on success
  463. sub ProcessX3FDirectory($$$)
  464. {
  465. my ($et, $dirInfo, $tagTablePtr) = @_;
  466. my $raf = $$dirInfo{RAF};
  467. my $verbose = $et->Options('Verbose');
  468. $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start';
  469. # parse the X3F directory structure
  470. my ($buff, $ver, $entries, $index, $dir);
  471. $raf->Read($buff, 12) == 12 or return 'Truncated X3F image';
  472. $buff =~ /^SECd/ or return 'Bad section header';
  473. ($ver, $entries) = unpack('x4V2', $buff);
  474. $verbose and $et->VerboseDir('X3F Subsection', $entries);
  475. $raf->Read($dir, $entries * 12) == $entries * 12 or return 'Truncated X3F directory';
  476. for ($index=0; $index<$entries; ++$index) {
  477. my $pos = $index * 12;
  478. my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
  479. my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
  480. if ($verbose) {
  481. $et->VPrint(0, "$$et{INDENT}$index) $tag Subsection ($len bytes):\n");
  482. if ($verbose > 2) {
  483. $raf->Seek($offset, 0) or return 'Error seeking';
  484. $raf->Read($buff, $len) == $len or return 'Truncated image';
  485. $et->VerboseDump(\$buff);
  486. }
  487. }
  488. next unless $tagInfo;
  489. $raf->Seek($offset, 0) or return "Error seeking for $$tagInfo{Name}";
  490. if ($$tagInfo{Name} eq 'PreviewImage') {
  491. # check image header to see if this is a JPEG preview image
  492. $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
  493. # ignore all image data but JPEG compressed (version 2.0, type 2, format 18)
  494. next unless $buff =~ /^SECi\0\0\x02\0\x02\0\0\0\x12\0\0\0/;
  495. # check preview image size and extract full-sized preview as JpgFromRaw
  496. if ($$et{ImageWidth} == unpack('x16V', $buff)) {
  497. $$et{IsJpgFromRaw} = 1;
  498. $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
  499. delete $$et{IsJpgFromRaw};
  500. }
  501. $offset += 28;
  502. $len -= 28;
  503. }
  504. $raf->Read($buff, $len) == $len or return "Error reading $$tagInfo{Name} data";
  505. my $subdir = $$tagInfo{SubDirectory};
  506. if ($subdir) {
  507. my %dirInfo = ( DataPt => \$buff );
  508. my $subTable = GetTagTable($$subdir{TagTable});
  509. $et->ProcessDirectory(\%dirInfo, $subTable);
  510. } else {
  511. # extract metadata from JpgFromRaw
  512. if ($$tagInfo{Name} eq 'JpgFromRaw') {
  513. my %dirInfo = (
  514. Parent => 'X3F',
  515. RAF => new File::RandomAccess(\$buff),
  516. );
  517. $$et{BASE} += $offset;
  518. $et->ProcessJPEG(\%dirInfo);
  519. $$et{BASE} -= $offset;
  520. SetByteOrder('II');
  521. }
  522. $et->FoundTag($tagInfo, $buff);
  523. }
  524. }
  525. return undef;
  526. }
  527. #------------------------------------------------------------------------------
  528. # Read/write information from a Sigma raw (X3F) image
  529. # Inputs: 0) ExifTool ref, 1) DirInfo ref
  530. # Returns: 1 on success, 0 if this wasn't a valid X3F image, or -1 on write error
  531. sub ProcessX3F($$)
  532. {
  533. my ($et, $dirInfo) = @_;
  534. my $outfile = $$dirInfo{OutFile};
  535. my $raf = $$dirInfo{RAF};
  536. my $warn = $outfile ? \&Image::ExifTool::Error : \&Image::ExifTool::Warn;
  537. my ($buff, $err, $hdrLen);
  538. return 0 unless $raf->Read($buff, 40) == 40;
  539. return 0 unless $buff =~ /^FOVb/;
  540. SetByteOrder('II');
  541. $et->SetFileType();
  542. # check version number
  543. my $ver = unpack('x4V',$buff);
  544. $ver = ($ver >> 16) . '.' . ($ver & 0xffff);
  545. if ($ver > 5) {
  546. &$warn($et, "Untested X3F version ($ver). Please submit sample for testing", 1);
  547. }
  548. # read version 2.1/2.2/2.3 extended header
  549. if ($ver > 2) {
  550. my ($extra, $buf2);
  551. if ($ver >= 4) {
  552. $hdrLen = 0x300;
  553. $extra = 0;
  554. } else {
  555. $hdrLen = $ver > 2.2 ? 104 : 72; # SceneCaptureType string added in 2.3
  556. $extra = 160; # (extended header is 160 bytes)
  557. }
  558. my $more = $hdrLen - length($buff) + $extra;
  559. unless ($raf->Read($buf2, $more) == $more) {
  560. &$warn($et, 'Error reading X3F header');
  561. return 1;
  562. }
  563. $buff .= $buf2;
  564. }
  565. my ($widPos, $hdrType) = $ver < 4 ? (28, 'Header') : (40, 'Header4');
  566. # extract ImageWidth for later
  567. $$et{ImageWidth} = Get32u(\$buff, $widPos);
  568. # process header information
  569. my $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::Main');
  570. unless ($outfile) {
  571. $et->HandleTag($tagTablePtr, $hdrType, $buff,
  572. DataPt => \$buff,
  573. Size => $hdrLen,
  574. );
  575. }
  576. # read the directory pointer
  577. $raf->Seek(-4, 2) or &$warn($et, 'Seek error'), return 1;
  578. unless ($raf->Read($buff, 4) == 4) {
  579. &$warn($et, 'Error reading X3F dir pointer');
  580. return 1;
  581. }
  582. my $offset = unpack('V', $buff);
  583. my %dirInfo = (
  584. RAF => $raf,
  585. DirStart => $offset,
  586. );
  587. if ($outfile) {
  588. $dirInfo{OutFile} = $outfile;
  589. $err = WriteX3F($et, \%dirInfo);
  590. return -1 if $err and $err eq '-1';
  591. } else {
  592. # process the X3F subsections
  593. $err = $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
  594. }
  595. $err and &$warn($et, $err);
  596. return 1;
  597. }
  598. 1; # end
  599. __END__
  600. =head1 NAME
  601. Image::ExifTool::SigmaRaw - Read Sigma/Foveon RAW (X3F) meta information
  602. =head1 SYNOPSIS
  603. This module is loaded automatically by Image::ExifTool when required.
  604. =head1 DESCRIPTION
  605. This module contains definitions required by Image::ExifTool to read
  606. Sigma and Foveon X3F images.
  607. =head1 AUTHOR
  608. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  609. This library is free software; you can redistribute it and/or modify it
  610. under the same terms as Perl itself.
  611. =head1 REFERENCES
  612. =over 4
  613. =item L<http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf>
  614. =back
  615. =head1 SEE ALSO
  616. L<Image::ExifTool::TagNames/SigmaRaw Tags>,
  617. L<Image::ExifTool::Sigma(3pm)|Image::ExifTool::Sigma>,
  618. L<Image::ExifTool(3pm)|Image::ExifTool>
  619. =cut