Home | History | Annotate | Download | only in source
      1 /*****************************************************************************/
      2 // Copyright 2006-2008 Adobe Systems Incorporated
      3 // All Rights Reserved.
      4 //
      5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
      6 // accordance with the terms of the Adobe license agreement accompanying it.
      7 /*****************************************************************************/
      8 
      9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_shared.cpp#2 $ */
     10 /* $DateTime: 2012/06/14 20:24:41 $ */
     11 /* $Change: 835078 $ */
     12 /* $Author: tknoll $ */
     13 
     14 /*****************************************************************************/
     15 
     16 #include "dng_shared.h"
     17 
     18 #include "dng_camera_profile.h"
     19 #include "dng_exceptions.h"
     20 #include "dng_globals.h"
     21 #include "dng_memory.h"
     22 #include "dng_parse_utils.h"
     23 #include "dng_safe_arithmetic.h"
     24 #include "dng_tag_codes.h"
     25 #include "dng_tag_types.h"
     26 #include "dng_tag_values.h"
     27 #include "dng_utils.h"
     28 
     29 /*****************************************************************************/
     30 
     31 dng_camera_profile_info::dng_camera_profile_info ()
     32 
     33 	:	fBigEndian (false)
     34 
     35 	,	fColorPlanes (0)
     36 
     37 	,	fCalibrationIlluminant1 (lsUnknown)
     38 	,	fCalibrationIlluminant2 (lsUnknown)
     39 
     40 	,	fColorMatrix1 ()
     41 	,	fColorMatrix2 ()
     42 
     43 	,	fForwardMatrix1 ()
     44 	,	fForwardMatrix2 ()
     45 
     46 	,	fReductionMatrix1 ()
     47 	,	fReductionMatrix2 ()
     48 
     49 	,	fProfileCalibrationSignature ()
     50 
     51 	,	fProfileName ()
     52 
     53 	,	fProfileCopyright ()
     54 
     55 	,	fEmbedPolicy (pepAllowCopying)
     56 
     57 	,	fProfileHues (0)
     58 	,	fProfileSats (0)
     59 	,	fProfileVals (0)
     60 
     61 	,	fHueSatDeltas1Offset (0)
     62 	,	fHueSatDeltas1Count  (0)
     63 
     64 	,	fHueSatDeltas2Offset (0)
     65 	,	fHueSatDeltas2Count  (0)
     66 
     67 	,	fHueSatMapEncoding (encoding_Linear)
     68 
     69 	,	fLookTableHues (0)
     70 	,	fLookTableSats (0)
     71 	,	fLookTableVals (0)
     72 
     73 	,	fLookTableOffset (0)
     74 	,	fLookTableCount  (0)
     75 
     76 	,	fLookTableEncoding (encoding_Linear)
     77 
     78 	,	fBaselineExposureOffset (0, 100)
     79 
     80 	,	fDefaultBlackRender (defaultBlackRender_Auto)
     81 
     82 	,	fToneCurveOffset     (0)
     83 	,	fToneCurveCount      (0)
     84 
     85 	,	fUniqueCameraModel ()
     86 
     87 	{
     88 
     89 	}
     90 
     91 /*****************************************************************************/
     92 
     93 dng_camera_profile_info::~dng_camera_profile_info ()
     94 	{
     95 
     96 	}
     97 
     98 /*****************************************************************************/
     99 
    100 bool dng_camera_profile_info::ParseTag (dng_stream &stream,
    101 										uint32 parentCode,
    102 										uint32 tagCode,
    103 										uint32 tagType,
    104 										uint32 tagCount,
    105 										uint64 tagOffset)
    106 	{
    107 
    108 	switch (tagCode)
    109 		{
    110 
    111 		case tcCalibrationIlluminant1:
    112 			{
    113 
    114 			CheckTagType (parentCode, tagCode, tagType, ttShort);
    115 
    116 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    117 
    118 			fCalibrationIlluminant1 = stream.TagValue_uint32 (tagType);
    119 
    120 			#if qDNGValidate
    121 
    122 			if (gVerbose)
    123 				{
    124 
    125 				printf ("CalibrationIlluminant1: %s\n",
    126 						LookupLightSource (fCalibrationIlluminant1));
    127 
    128 				}
    129 
    130 			#endif
    131 
    132 			break;
    133 
    134 			}
    135 
    136 		case tcCalibrationIlluminant2:
    137 			{
    138 
    139 			CheckTagType (parentCode, tagCode, tagType, ttShort);
    140 
    141 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    142 
    143 			fCalibrationIlluminant2 = stream.TagValue_uint32 (tagType);
    144 
    145 			#if qDNGValidate
    146 
    147 			if (gVerbose)
    148 				{
    149 
    150 				printf ("CalibrationIlluminant2: %s\n",
    151 						LookupLightSource (fCalibrationIlluminant2));
    152 
    153 				}
    154 
    155 			#endif
    156 
    157 			break;
    158 
    159 			}
    160 
    161 		case tcColorMatrix1:
    162 			{
    163 
    164 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    165 
    166 			if (fColorPlanes == 0)
    167 				{
    168 
    169 				fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes);
    170 
    171 				}
    172 
    173 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    174 				return false;
    175 
    176 			if (!ParseMatrixTag (stream,
    177 								 parentCode,
    178 								 tagCode,
    179 								 tagType,
    180 								 tagCount,
    181 								 fColorPlanes,
    182 								 3,
    183 								 fColorMatrix1))
    184 				return false;
    185 
    186 			#if qDNGValidate
    187 
    188 			if (gVerbose)
    189 				{
    190 
    191 				printf ("ColorMatrix1:\n");
    192 
    193 				DumpMatrix (fColorMatrix1);
    194 
    195 				}
    196 
    197 			#endif
    198 
    199 			break;
    200 
    201 			}
    202 
    203 		case tcColorMatrix2:
    204 			{
    205 
    206 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    207 
    208 			// Kludge - Hasselblad FFF files are very DNG-like, but sometimes
    209 			// only have a ColorMatrix2 tag and no ColorMatrix1 tag.
    210 
    211 			bool hasselbladHack = (fColorPlanes == 0);
    212 
    213 			if (hasselbladHack)
    214 				{
    215 
    216 				fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes);
    217 
    218 				#if qDNGValidate
    219 
    220 				ReportWarning ("ColorMatrix2 without ColorMatrix1");
    221 
    222 				#endif
    223 
    224 				}
    225 
    226 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    227 				return false;
    228 
    229 			if (!ParseMatrixTag (stream,
    230 								 parentCode,
    231 								 tagCode,
    232 								 tagType,
    233 								 tagCount,
    234 								 fColorPlanes,
    235 								 3,
    236 								 fColorMatrix2))
    237 				return false;
    238 
    239 			#if qDNGValidate
    240 
    241 			if (gVerbose)
    242 				{
    243 
    244 				printf ("ColorMatrix2:\n");
    245 
    246 				DumpMatrix (fColorMatrix2);
    247 
    248 				}
    249 
    250 			#endif
    251 
    252 			if (hasselbladHack)
    253 				{
    254 
    255 				fColorMatrix1 = fColorMatrix2;
    256 
    257 				fColorMatrix2 = dng_matrix ();
    258 
    259 				}
    260 
    261 			break;
    262 
    263 			}
    264 
    265 		case tcForwardMatrix1:
    266 			{
    267 
    268 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    269 
    270 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    271 				return false;
    272 
    273 			if (!ParseMatrixTag (stream,
    274 								 parentCode,
    275 								 tagCode,
    276 								 tagType,
    277 								 tagCount,
    278 								 3,
    279 								 fColorPlanes,
    280 								 fForwardMatrix1))
    281 				return false;
    282 
    283 			#if qDNGValidate
    284 
    285 			if (gVerbose)
    286 				{
    287 
    288 				printf ("ForwardMatrix1:\n");
    289 
    290 				DumpMatrix (fForwardMatrix1);
    291 
    292 				}
    293 
    294 			#endif
    295 
    296 			break;
    297 
    298 			}
    299 
    300 		case tcForwardMatrix2:
    301 			{
    302 
    303 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    304 
    305 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    306 				return false;
    307 
    308 			if (!ParseMatrixTag (stream,
    309 								 parentCode,
    310 								 tagCode,
    311 								 tagType,
    312 								 tagCount,
    313 								 3,
    314 								 fColorPlanes,
    315 								 fForwardMatrix2))
    316 				return false;
    317 
    318 			#if qDNGValidate
    319 
    320 			if (gVerbose)
    321 				{
    322 
    323 				printf ("ForwardMatrix2:\n");
    324 
    325 				DumpMatrix (fForwardMatrix2);
    326 
    327 				}
    328 
    329 			#endif
    330 
    331 			break;
    332 
    333 			}
    334 
    335 		case tcReductionMatrix1:
    336 			{
    337 
    338 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    339 
    340 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    341 				return false;
    342 
    343 			if (!ParseMatrixTag (stream,
    344 								 parentCode,
    345 								 tagCode,
    346 								 tagType,
    347 								 tagCount,
    348 								 3,
    349 								 fColorPlanes,
    350 								 fReductionMatrix1))
    351 				return false;
    352 
    353 			#if qDNGValidate
    354 
    355 			if (gVerbose)
    356 				{
    357 
    358 				printf ("ReductionMatrix1:\n");
    359 
    360 				DumpMatrix (fReductionMatrix1);
    361 
    362 				}
    363 
    364 			#endif
    365 
    366 			break;
    367 
    368 			}
    369 
    370 		case tcReductionMatrix2:
    371 			{
    372 
    373 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    374 
    375 			if (!CheckColorImage (parentCode, tagCode, fColorPlanes))
    376 				return false;
    377 
    378 			if (!ParseMatrixTag (stream,
    379 								 parentCode,
    380 								 tagCode,
    381 								 tagType,
    382 								 tagCount,
    383 								 3,
    384 								 fColorPlanes,
    385 								 fReductionMatrix2))
    386 				return false;
    387 
    388 			#if qDNGValidate
    389 
    390 			if (gVerbose)
    391 				{
    392 
    393 				printf ("ReductionMatrix2:\n");
    394 
    395 				DumpMatrix (fReductionMatrix2);
    396 
    397 				}
    398 
    399 			#endif
    400 
    401 			break;
    402 
    403 			}
    404 
    405 		case tcProfileCalibrationSignature:
    406 			{
    407 
    408 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
    409 
    410 			ParseStringTag (stream,
    411 							parentCode,
    412 							tagCode,
    413 							tagCount,
    414 							fProfileCalibrationSignature,
    415 							false);
    416 
    417 			#if qDNGValidate
    418 
    419 			if (gVerbose)
    420 				{
    421 
    422 				printf ("ProfileCalibrationSignature: ");
    423 
    424 				DumpString (fProfileCalibrationSignature);
    425 
    426 				printf ("\n");
    427 
    428 				}
    429 
    430 			#endif
    431 
    432 			break;
    433 
    434 			}
    435 
    436 		case tcProfileName:
    437 			{
    438 
    439 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
    440 
    441 			ParseStringTag (stream,
    442 							parentCode,
    443 							tagCode,
    444 							tagCount,
    445 							fProfileName,
    446 							false);
    447 
    448 			#if qDNGValidate
    449 
    450 			if (gVerbose)
    451 				{
    452 
    453 				printf ("ProfileName: ");
    454 
    455 				DumpString (fProfileName);
    456 
    457 				printf ("\n");
    458 
    459 				}
    460 
    461 			#endif
    462 
    463 			break;
    464 
    465 			}
    466 
    467 		case tcProfileCopyright:
    468 			{
    469 
    470 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
    471 
    472 			ParseStringTag (stream,
    473 							parentCode,
    474 							tagCode,
    475 							tagCount,
    476 							fProfileCopyright,
    477 							false);
    478 
    479 			#if qDNGValidate
    480 
    481 			if (gVerbose)
    482 				{
    483 
    484 				printf ("ProfileCopyright: ");
    485 
    486 				DumpString (fProfileCopyright);
    487 
    488 				printf ("\n");
    489 
    490 				}
    491 
    492 			#endif
    493 
    494 			break;
    495 
    496 			}
    497 
    498 		case tcProfileEmbedPolicy:
    499 			{
    500 
    501 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    502 
    503 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    504 
    505 			fEmbedPolicy = stream.TagValue_uint32 (tagType);
    506 
    507 			#if qDNGValidate
    508 
    509 			if (gVerbose)
    510 				{
    511 
    512 				const char *policy;
    513 
    514 				switch (fEmbedPolicy)
    515 					{
    516 
    517 					case pepAllowCopying:
    518 						policy = "Allow copying";
    519 						break;
    520 
    521 					case pepEmbedIfUsed:
    522 						policy = "Embed if used";
    523 						break;
    524 
    525 					case pepEmbedNever:
    526 						policy = "Embed never";
    527 						break;
    528 
    529 					case pepNoRestrictions:
    530 						policy = "No restrictions";
    531 						break;
    532 
    533 					default:
    534 						policy = "INVALID VALUE";
    535 
    536 					}
    537 
    538 				printf ("ProfileEmbedPolicy: %s\n", policy);
    539 
    540 				}
    541 
    542 			#endif
    543 
    544 			break;
    545 
    546 			}
    547 
    548 		case tcProfileHueSatMapDims:
    549 			{
    550 
    551 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    552 
    553 			CheckTagCount (parentCode, tagCode, tagCount, 2, 3);
    554 
    555 			fProfileHues = stream.TagValue_uint32 (tagType);
    556 			fProfileSats = stream.TagValue_uint32 (tagType);
    557 
    558 			if (tagCount > 2)
    559 				fProfileVals = stream.TagValue_uint32 (tagType);
    560 			else
    561 				fProfileVals = 1;
    562 
    563 			#if qDNGValidate
    564 
    565 			if (gVerbose)
    566 				{
    567 
    568 				printf ("ProfileHueSatMapDims: Hues = %u, Sats = %u, Vals = %u\n",
    569 						(unsigned) fProfileHues,
    570 						(unsigned) fProfileSats,
    571 						(unsigned) fProfileVals);
    572 
    573 				}
    574 
    575 			#endif
    576 
    577 			break;
    578 
    579 			}
    580 
    581 		case tcProfileHueSatMapData1:
    582 			{
    583 
    584 			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
    585 				return false;
    586 
    587 			bool skipSat0 = (tagCount ==
    588 							 SafeUint32Mult(fProfileHues,
    589 											SafeUint32Sub(fProfileSats, 1u),
    590 											fProfileVals,
    591 											3u));
    592 
    593 			if (!skipSat0)
    594 				{
    595 
    596 				if (!CheckTagCount (parentCode, tagCode, tagCount,
    597 									SafeUint32Mult(fProfileHues, fProfileSats, fProfileVals, 3)))
    598 					return false;
    599 
    600 				}
    601 
    602 			fBigEndian = stream.BigEndian ();
    603 
    604 			fHueSatDeltas1Offset = tagOffset;
    605 			fHueSatDeltas1Count  = tagCount;
    606 
    607 			#if qDNGValidate
    608 
    609 			if (gVerbose)
    610 				{
    611 
    612 				printf ("ProfileHueSatMapData1:\n");
    613 
    614 				DumpHueSatMap (stream,
    615 							   fProfileHues,
    616 							   fProfileSats,
    617 							   fProfileVals,
    618 							   skipSat0);
    619 
    620 				}
    621 
    622 			#endif
    623 
    624 			break;
    625 
    626 			}
    627 
    628 		case tcProfileHueSatMapData2:
    629 			{
    630 
    631 			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
    632 				return false;
    633 
    634 			bool skipSat0 = (tagCount ==
    635 							 SafeUint32Mult(fProfileHues,
    636 											SafeUint32Sub(fProfileSats, 1u),
    637 											fProfileVals,
    638 											3u));
    639 
    640 			if (!skipSat0)
    641 				{
    642 
    643 				if (!CheckTagCount (parentCode, tagCode, tagCount,
    644 									SafeUint32Mult(fProfileHues, fProfileSats, fProfileVals, 3)))
    645 					return false;
    646 
    647 				}
    648 
    649 			fBigEndian = stream.BigEndian ();
    650 
    651 			fHueSatDeltas2Offset = tagOffset;
    652 			fHueSatDeltas2Count  = tagCount;
    653 
    654 			#if qDNGValidate
    655 
    656 			if (gVerbose)
    657 				{
    658 
    659 				printf ("ProfileHueSatMapData2:\n");
    660 
    661 				DumpHueSatMap (stream,
    662 							   fProfileHues,
    663 							   fProfileSats,
    664 							   fProfileVals,
    665 							   skipSat0);
    666 
    667 				}
    668 
    669 			#endif
    670 
    671 			break;
    672 
    673 			}
    674 
    675 		case tcProfileHueSatMapEncoding:
    676 			{
    677 
    678 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    679 
    680 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    681 
    682 			fHueSatMapEncoding = stream.TagValue_uint32 (tagType);
    683 
    684 			#if qDNGValidate
    685 
    686 			if (gVerbose)
    687 				{
    688 
    689 				const char *encoding = NULL;
    690 
    691 				switch (fHueSatMapEncoding)
    692 					{
    693 
    694 					case encoding_Linear:
    695 						encoding = "Linear";
    696 						break;
    697 
    698 					case encoding_sRGB:
    699 						encoding = "sRGB";
    700 						break;
    701 
    702 					default:
    703 						encoding = "INVALID VALUE";
    704 
    705 					}
    706 
    707 				printf ("ProfileHueSatMapEncoding: %s\n", encoding);
    708 
    709 				}
    710 
    711 			#endif
    712 
    713 			break;
    714 
    715 			}
    716 
    717 		case tcProfileLookTableDims:
    718 			{
    719 
    720 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    721 
    722 			CheckTagCount (parentCode, tagCode, tagCount, 2, 3);
    723 
    724 			fLookTableHues = stream.TagValue_uint32 (tagType);
    725 			fLookTableSats = stream.TagValue_uint32 (tagType);
    726 
    727 			if (tagCount > 2)
    728 				fLookTableVals = stream.TagValue_uint32 (tagType);
    729 			else
    730 				fLookTableVals = 1;
    731 
    732 			#if qDNGValidate
    733 
    734 			if (gVerbose)
    735 				{
    736 
    737 				printf ("ProfileLookTableDims: Hues = %u, Sats = %u, Vals = %u\n",
    738 						(unsigned) fLookTableHues,
    739 						(unsigned) fLookTableSats,
    740 						(unsigned) fLookTableVals);
    741 
    742 				}
    743 
    744 			#endif
    745 
    746 			break;
    747 
    748 			}
    749 
    750 		case tcProfileLookTableData:
    751 			{
    752 
    753 			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
    754 				return false;
    755 
    756 			bool skipSat0 = (tagCount ==
    757 							 SafeUint32Mult(fLookTableHues,
    758 											SafeUint32Sub(fLookTableSats, 1u),
    759 											fLookTableVals,
    760 											3u));
    761 
    762 			if (!skipSat0)
    763 				{
    764 
    765 				if (!CheckTagCount (parentCode, tagCode, tagCount,
    766 									SafeUint32Mult(fLookTableHues,
    767 													fLookTableSats,
    768 													fLookTableVals, 3)))
    769 					return false;
    770 
    771 				}
    772 
    773 			fBigEndian = stream.BigEndian ();
    774 
    775 			fLookTableOffset = tagOffset;
    776 			fLookTableCount  = tagCount;
    777 
    778 			#if qDNGValidate
    779 
    780 			if (gVerbose)
    781 				{
    782 
    783 				printf ("ProfileLookTableData:\n");
    784 
    785 				DumpHueSatMap (stream,
    786 							   fLookTableHues,
    787 							   fLookTableSats,
    788 							   fLookTableVals,
    789 							   skipSat0);
    790 
    791 				}
    792 
    793 			#endif
    794 
    795 			break;
    796 
    797 			}
    798 
    799 		case tcProfileLookTableEncoding:
    800 			{
    801 
    802 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    803 
    804 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    805 
    806 			fLookTableEncoding = stream.TagValue_uint32 (tagType);
    807 
    808 			#if qDNGValidate
    809 
    810 			if (gVerbose)
    811 				{
    812 
    813 				const char *encoding = NULL;
    814 
    815 				switch (fLookTableEncoding)
    816 					{
    817 
    818 					case encoding_Linear:
    819 						encoding = "Linear";
    820 						break;
    821 
    822 					case encoding_sRGB:
    823 						encoding = "sRGB";
    824 						break;
    825 
    826 					default:
    827 						encoding = "INVALID VALUE";
    828 
    829 					}
    830 
    831 				printf ("ProfileLookTableEncoding: %s\n", encoding);
    832 
    833 				}
    834 
    835 			#endif
    836 
    837 			break;
    838 
    839 			}
    840 
    841 		case tcBaselineExposureOffset:
    842 			{
    843 
    844 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
    845 
    846 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    847 
    848 			fBaselineExposureOffset = stream.TagValue_srational (tagType);
    849 
    850 			#if qDNGValidate
    851 
    852 			if (gVerbose)
    853 				{
    854 
    855 				printf ("BaselineExposureOffset: %+0.2f\n",
    856 					    fBaselineExposureOffset.As_real64 ());
    857 
    858 				}
    859 
    860 			#endif
    861 
    862 			break;
    863 
    864 			}
    865 
    866 		case tcDefaultBlackRender:
    867 			{
    868 
    869 			CheckTagType (parentCode, tagCode, tagType, ttLong);
    870 
    871 			CheckTagCount (parentCode, tagCode, tagCount, 1);
    872 
    873 			fDefaultBlackRender = stream.TagValue_uint32 (tagType);
    874 
    875 			#if qDNGValidate
    876 
    877 			if (gVerbose)
    878 				{
    879 
    880 				const char *setting = NULL;
    881 
    882 				switch (fDefaultBlackRender)
    883 					{
    884 
    885 					case defaultBlackRender_Auto:
    886 						setting = "Auto";
    887 						break;
    888 
    889 					case defaultBlackRender_None:
    890 						setting = "None";
    891 						break;
    892 
    893 					default:
    894 						setting = "INVALID VALUE";
    895 
    896 					}
    897 
    898 				printf ("DefaultBlackRender: %s\n",
    899 						setting);
    900 
    901 				}
    902 
    903 			#endif
    904 
    905 			break;
    906 
    907 			}
    908 
    909 		case tcProfileToneCurve:
    910 			{
    911 
    912 			if (!CheckTagType (parentCode, tagCode, tagType, ttFloat))
    913 				return false;
    914 
    915 			if (!CheckTagCount (parentCode, tagCode, tagCount, 4, tagCount))
    916 				return false;
    917 
    918 			if ((tagCount & 1) != 0)
    919 				{
    920 
    921 				#if qDNGValidate
    922 
    923 					{
    924 
    925 					char message [256];
    926 
    927 					sprintf (message,
    928 							 "%s %s has odd count (%u)",
    929 							 LookupParentCode (parentCode),
    930 							 LookupTagCode (parentCode, tagCode),
    931 							 (unsigned) tagCount);
    932 
    933 					ReportWarning (message);
    934 
    935 					}
    936 
    937 				#endif
    938 
    939 				return false;
    940 
    941 				}
    942 
    943 			fBigEndian = stream.BigEndian ();
    944 
    945 			fToneCurveOffset = tagOffset;
    946 			fToneCurveCount  = tagCount;
    947 
    948 			#if qDNGValidate
    949 
    950 			if (gVerbose)
    951 				{
    952 
    953 				DumpTagValues (stream,
    954 							   "Coord",
    955 							   parentCode,
    956 							   tagCode,
    957 							   tagType,
    958 							   tagCount);
    959 
    960 
    961 				}
    962 
    963 			#endif
    964 
    965 			break;
    966 
    967 			}
    968 
    969 		case tcUniqueCameraModel:
    970 			{
    971 
    972 			// Note: This code is only used when parsing stand-alone
    973 			// profiles.  The embedded profiles are assumed to be restricted
    974 			// to the model they are embedded in.
    975 
    976 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
    977 
    978 			ParseStringTag (stream,
    979 							parentCode,
    980 							tagCode,
    981 							tagCount,
    982 							fUniqueCameraModel,
    983 							false);
    984 
    985 			bool didTrim = fUniqueCameraModel.TrimTrailingBlanks ();
    986 
    987 			#if qDNGValidate
    988 
    989 			if (didTrim)
    990 				{
    991 
    992 				ReportWarning ("UniqueCameraModel string has trailing blanks");
    993 
    994 				}
    995 
    996 			if (gVerbose)
    997 				{
    998 
    999 				printf ("UniqueCameraModel: ");
   1000 
   1001 				DumpString (fUniqueCameraModel);
   1002 
   1003 				printf ("\n");
   1004 
   1005 				}
   1006 
   1007 			#else
   1008 
   1009 			(void) didTrim;		// Unused
   1010 
   1011 			#endif
   1012 
   1013 			break;
   1014 
   1015 			}
   1016 
   1017 		default:
   1018 			{
   1019 
   1020 			return false;
   1021 
   1022 			}
   1023 
   1024 		}
   1025 
   1026 	return true;
   1027 
   1028 	}
   1029 
   1030 /*****************************************************************************/
   1031 
   1032 bool dng_camera_profile_info::ParseExtended (dng_stream &stream)
   1033 	{
   1034 
   1035 	try
   1036 		{
   1037 
   1038 		// Offsets are relative to the start of this structure, not the entire file.
   1039 
   1040 		uint64 startPosition = stream.Position ();
   1041 
   1042 		// Read header. Like a TIFF header, but with different magic number
   1043 		// Plus all offsets are relative to the start of the IFD, not to the
   1044 		// stream or file.
   1045 
   1046 		uint16 byteOrder = stream.Get_uint16 ();
   1047 
   1048 		if (byteOrder == byteOrderMM)
   1049 			fBigEndian = true;
   1050 
   1051 		else if (byteOrder == byteOrderII)
   1052 			fBigEndian = false;
   1053 
   1054 		else
   1055 			return false;
   1056 
   1057 		TempBigEndian setEndianness (stream, fBigEndian);
   1058 
   1059 		uint16 magicNumber = stream.Get_uint16 ();
   1060 
   1061 		if (magicNumber != magicExtendedProfile)
   1062 			{
   1063 			return false;
   1064 			}
   1065 
   1066 		uint32 offset = stream.Get_uint32 ();
   1067 
   1068 		stream.Skip (SafeUint32Sub(offset, 8u));
   1069 
   1070 		// Start on IFD entries.
   1071 
   1072 		uint32 ifdEntries = stream.Get_uint16 ();
   1073 
   1074 		if (ifdEntries < 1)
   1075 			{
   1076 			return false;
   1077 			}
   1078 
   1079 		for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++)
   1080 			{
   1081 
   1082 			stream.SetReadPosition (startPosition + 8 + 2 + tag_index * 12);
   1083 
   1084 			uint16 tagCode  = stream.Get_uint16 ();
   1085 			uint32 tagType  = stream.Get_uint16 ();
   1086 			uint32 tagCount = stream.Get_uint32 ();
   1087 
   1088 			uint64 tagOffset = stream.Position ();
   1089 
   1090 			if (SafeUint32Mult(TagTypeSize (tagType), tagCount) > 4)
   1091 				{
   1092 
   1093 				tagOffset = startPosition + stream.Get_uint32 ();
   1094 
   1095 				stream.SetReadPosition (tagOffset);
   1096 
   1097 				}
   1098 
   1099 			if (!ParseTag (stream,
   1100 						   0,
   1101 						   tagCode,
   1102 						   tagType,
   1103 						   tagCount,
   1104 						   tagOffset))
   1105 				{
   1106 
   1107 				#if qDNGValidate
   1108 
   1109 				if (gVerbose)
   1110 					{
   1111 
   1112 					stream.SetReadPosition (tagOffset);
   1113 
   1114 					printf ("*");
   1115 
   1116 					DumpTagValues (stream,
   1117 								   LookupTagType (tagType),
   1118 								   0,
   1119 								   tagCode,
   1120 								   tagType,
   1121 								   tagCount);
   1122 
   1123 					}
   1124 
   1125 				#endif
   1126 
   1127 				}
   1128 
   1129 			}
   1130 
   1131 		return true;
   1132 
   1133 		}
   1134 
   1135 	catch (...)
   1136 		{
   1137 
   1138 		// Eat parsing errors.
   1139 
   1140 		}
   1141 
   1142 	return false;
   1143 
   1144 	}
   1145 
   1146 /*****************************************************************************/
   1147 
   1148 dng_shared::dng_shared ()
   1149 
   1150 	:	fExifIFD 			 (0)
   1151 	,	fGPSInfo 			 (0)
   1152 	,	fInteroperabilityIFD (0)
   1153 	,	fKodakDCRPrivateIFD  (0)
   1154 	,	fKodakKDCPrivateIFD  (0)
   1155 
   1156 	,	fXMPCount  (0)
   1157 	,	fXMPOffset (0)
   1158 
   1159 	,	fIPTC_NAA_Count  (0)
   1160 	,	fIPTC_NAA_Offset (0)
   1161 
   1162 	,	fMakerNoteCount  (0)
   1163 	,	fMakerNoteOffset (0)
   1164 	,	fMakerNoteSafety (0)
   1165 
   1166 	,	fDNGVersion         (0)
   1167 	,	fDNGBackwardVersion (0)
   1168 
   1169 	,	fUniqueCameraModel    ()
   1170 	,	fLocalizedCameraModel ()
   1171 
   1172 	,	fCameraProfile ()
   1173 
   1174 	,	fExtraCameraProfiles ()
   1175 
   1176 	,	fCameraCalibration1 ()
   1177 	,	fCameraCalibration2 ()
   1178 
   1179 	,	fCameraCalibrationSignature  ()
   1180 
   1181 	,	fAnalogBalance ()
   1182 
   1183 	,	fAsShotNeutral ()
   1184 
   1185 	,	fAsShotWhiteXY ()
   1186 
   1187 	,	fBaselineExposure      (0, 1)
   1188 	,	fBaselineNoise         (1, 1)
   1189 	,	fNoiseReductionApplied (0, 0)
   1190 	,	fBaselineSharpness     (1, 1)
   1191 	,	fLinearResponseLimit   (1, 1)
   1192 	,	fShadowScale           (1, 1)
   1193 
   1194 	,	fHasBaselineExposure (false)
   1195 	,	fHasShadowScale      (false)
   1196 
   1197 	,	fDNGPrivateDataCount  (0)
   1198 	,	fDNGPrivateDataOffset (0)
   1199 
   1200 	,	fRawImageDigest    ()
   1201 	,	fNewRawImageDigest ()
   1202 
   1203 	,	fRawDataUniqueID ()
   1204 
   1205 	,	fOriginalRawFileName ()
   1206 
   1207 	,	fOriginalRawFileDataCount  (0)
   1208 	,	fOriginalRawFileDataOffset (0)
   1209 
   1210 	,	fOriginalRawFileDigest ()
   1211 
   1212 	,	fAsShotICCProfileCount  (0)
   1213 	,	fAsShotICCProfileOffset (0)
   1214 
   1215 	,	fAsShotPreProfileMatrix ()
   1216 
   1217 	,	fCurrentICCProfileCount  (0)
   1218 	,	fCurrentICCProfileOffset (0)
   1219 
   1220 	,	fCurrentPreProfileMatrix ()
   1221 
   1222 	,	fColorimetricReference (crSceneReferred)
   1223 
   1224 	,	fAsShotProfileName ()
   1225 
   1226 	,	fNoiseProfile ()
   1227 
   1228 	,	fOriginalDefaultFinalSize     ()
   1229 	,	fOriginalBestQualityFinalSize ()
   1230 
   1231 	,	fOriginalDefaultCropSizeH ()
   1232 	,	fOriginalDefaultCropSizeV ()
   1233 
   1234 	{
   1235 
   1236 	}
   1237 
   1238 /*****************************************************************************/
   1239 
   1240 dng_shared::~dng_shared ()
   1241 	{
   1242 
   1243 	}
   1244 
   1245 /*****************************************************************************/
   1246 
   1247 bool dng_shared::ParseTag (dng_stream &stream,
   1248 						   dng_exif &exif,
   1249 						   uint32 parentCode,
   1250 						   bool /* isMainIFD */,
   1251 						   uint32 tagCode,
   1252 						   uint32 tagType,
   1253 						   uint32 tagCount,
   1254 						   uint64 tagOffset,
   1255 						   int64 /* offsetDelta */)
   1256 	{
   1257 
   1258 	if (parentCode == 0)
   1259 		{
   1260 
   1261 		if (Parse_ifd0 (stream,
   1262 						exif,
   1263 						parentCode,
   1264 						tagCode,
   1265 						tagType,
   1266 						tagCount,
   1267 						tagOffset))
   1268 			{
   1269 
   1270 			return true;
   1271 
   1272 			}
   1273 
   1274 		}
   1275 
   1276 	if (parentCode == 0 ||
   1277 		parentCode == tcExifIFD)
   1278 		{
   1279 
   1280 		if (Parse_ifd0_exif (stream,
   1281 							 exif,
   1282 						 	 parentCode,
   1283 						 	 tagCode,
   1284 						 	 tagType,
   1285 						 	 tagCount,
   1286 						 	 tagOffset))
   1287 			{
   1288 
   1289 			return true;
   1290 
   1291 			}
   1292 
   1293 		}
   1294 
   1295 	return false;
   1296 
   1297 	}
   1298 
   1299 /*****************************************************************************/
   1300 
   1301 // Parses tags that should only appear in IFD 0.
   1302 
   1303 bool dng_shared::Parse_ifd0 (dng_stream &stream,
   1304 							 dng_exif & /* exif */,
   1305 							 uint32 parentCode,
   1306 							 uint32 tagCode,
   1307 							 uint32 tagType,
   1308 							 uint32 tagCount,
   1309 							 uint64 tagOffset)
   1310 	{
   1311 
   1312 	switch (tagCode)
   1313 		{
   1314 
   1315 		case tcXMP:
   1316 			{
   1317 
   1318 			CheckTagType (parentCode, tagCode, tagType, ttByte, ttUndefined);
   1319 
   1320 			fXMPCount  = tagCount;
   1321 			fXMPOffset = fXMPCount ? tagOffset : 0;
   1322 
   1323 			#if qDNGValidate
   1324 
   1325 			if (gVerbose)
   1326 				{
   1327 
   1328 				printf ("XMP: Count = %u, Offset = %u\n",
   1329 						(unsigned) fXMPCount,
   1330 						(unsigned) fXMPOffset);
   1331 
   1332 				if (fXMPCount)
   1333 					{
   1334 
   1335 					DumpXMP (stream, fXMPCount);
   1336 
   1337 					}
   1338 
   1339 				}
   1340 
   1341 			#endif
   1342 
   1343 			break;
   1344 
   1345 			}
   1346 
   1347 		case tcIPTC_NAA:
   1348 			{
   1349 
   1350 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttAscii, ttUndefined);
   1351 
   1352 			fIPTC_NAA_Count = SafeUint32Mult(tagCount,
   1353 														  TagTypeSize(tagType));
   1354 			fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0;
   1355 
   1356 			#if qDNGValidate
   1357 
   1358 			if (gVerbose)
   1359 				{
   1360 
   1361 				printf ("IPTC/NAA: Count = %u, Offset = %u\n",
   1362 						(unsigned) fIPTC_NAA_Count,
   1363 						(unsigned) fIPTC_NAA_Offset);
   1364 
   1365 				if (fIPTC_NAA_Count)
   1366 					{
   1367 
   1368 					DumpHexAscii (stream, fIPTC_NAA_Count);
   1369 
   1370 					}
   1371 
   1372 				// Compute and output the digest.
   1373 
   1374 				dng_memory_data buffer (fIPTC_NAA_Count);
   1375 
   1376 				stream.SetReadPosition (fIPTC_NAA_Offset);
   1377 
   1378 				stream.Get (buffer.Buffer (), fIPTC_NAA_Count);
   1379 
   1380 				const uint8 *data = buffer.Buffer_uint8 ();
   1381 
   1382 				uint32 count = fIPTC_NAA_Count;
   1383 
   1384 				// Method 1: Counting all bytes (this is correct).
   1385 
   1386 					{
   1387 
   1388 					dng_md5_printer printer;
   1389 
   1390 					printer.Process (data, count);
   1391 
   1392 					printf ("IPTCDigest: ");
   1393 
   1394 					DumpFingerprint (printer.Result ());
   1395 
   1396 					printf ("\n");
   1397 
   1398 					}
   1399 
   1400 				// Method 2: Ignoring zero padding.
   1401 
   1402 					{
   1403 
   1404 					uint32 removed = 0;
   1405 
   1406 					while ((removed < 3) && (count > 0) && (data [count - 1] == 0))
   1407 						{
   1408 						removed++;
   1409 						count--;
   1410 						}
   1411 
   1412 					if (removed != 0)
   1413 						{
   1414 
   1415 						dng_md5_printer printer;
   1416 
   1417 						printer.Process (data, count);
   1418 
   1419 						printf ("IPTCDigest (ignoring zero padding): ");
   1420 
   1421 						DumpFingerprint (printer.Result ());
   1422 
   1423 						printf ("\n");
   1424 
   1425 						}
   1426 
   1427 					}
   1428 
   1429 				}
   1430 
   1431 			#endif
   1432 
   1433 			break;
   1434 
   1435 			}
   1436 
   1437 		case tcExifIFD:
   1438 			{
   1439 
   1440 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
   1441 
   1442 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1443 
   1444 			fExifIFD = stream.TagValue_uint32 (tagType);
   1445 
   1446 			#if qDNGValidate
   1447 
   1448 			if (gVerbose)
   1449 				{
   1450 				printf ("ExifIFD: %u\n", (unsigned) fExifIFD);
   1451 				}
   1452 
   1453 			#endif
   1454 
   1455 			break;
   1456 
   1457 			}
   1458 
   1459 		case tcGPSInfo:
   1460 			{
   1461 
   1462 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
   1463 
   1464 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1465 
   1466 			fGPSInfo = stream.TagValue_uint32 (tagType);
   1467 
   1468 			#if qDNGValidate
   1469 
   1470 			if (gVerbose)
   1471 				{
   1472 				printf ("GPSInfo: %u\n", (unsigned) fGPSInfo);
   1473 				}
   1474 
   1475 			#endif
   1476 
   1477 			break;
   1478 
   1479 			}
   1480 
   1481 		case tcKodakDCRPrivateIFD:
   1482 			{
   1483 
   1484 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
   1485 
   1486 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1487 
   1488 			fKodakDCRPrivateIFD = stream.TagValue_uint32 (tagType);
   1489 
   1490 			#if qDNGValidate
   1491 
   1492 			if (gVerbose)
   1493 				{
   1494 				printf ("KodakDCRPrivateIFD: %u\n", (unsigned) fKodakDCRPrivateIFD);
   1495 				}
   1496 
   1497 			#endif
   1498 
   1499 			break;
   1500 
   1501 			}
   1502 
   1503 		case tcKodakKDCPrivateIFD:
   1504 			{
   1505 
   1506 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
   1507 
   1508 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1509 
   1510 			fKodakKDCPrivateIFD = stream.TagValue_uint32 (tagType);
   1511 
   1512 			#if qDNGValidate
   1513 
   1514 			if (gVerbose)
   1515 				{
   1516 				printf ("KodakKDCPrivateIFD: %u\n", (unsigned) fKodakKDCPrivateIFD);
   1517 				}
   1518 
   1519 			#endif
   1520 
   1521 			break;
   1522 
   1523 			}
   1524 
   1525 		case tcDNGVersion:
   1526 			{
   1527 
   1528 			CheckTagType (parentCode, tagCode, tagType, ttByte);
   1529 
   1530 			CheckTagCount (parentCode, tagCode, tagCount, 4);
   1531 
   1532 			uint32 b0 = stream.Get_uint8 ();
   1533 			uint32 b1 = stream.Get_uint8 ();
   1534 			uint32 b2 = stream.Get_uint8 ();
   1535 			uint32 b3 = stream.Get_uint8 ();
   1536 
   1537 			fDNGVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
   1538 
   1539 			#if qDNGValidate
   1540 
   1541 			if (gVerbose)
   1542 				{
   1543 				printf ("DNGVersion: %u.%u.%u.%u\n",
   1544 						(unsigned) b0,
   1545 						(unsigned) b1,
   1546 						(unsigned) b2,
   1547 						(unsigned) b3);
   1548 				}
   1549 
   1550 			#endif
   1551 
   1552 			break;
   1553 
   1554 			}
   1555 
   1556 		case tcDNGBackwardVersion:
   1557 			{
   1558 
   1559 			CheckTagType (parentCode, tagCode, tagType, ttByte);
   1560 
   1561 			CheckTagCount (parentCode, tagCode, tagCount, 4);
   1562 
   1563 			uint32 b0 = stream.Get_uint8 ();
   1564 			uint32 b1 = stream.Get_uint8 ();
   1565 			uint32 b2 = stream.Get_uint8 ();
   1566 			uint32 b3 = stream.Get_uint8 ();
   1567 
   1568 			fDNGBackwardVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
   1569 
   1570 			#if qDNGValidate
   1571 
   1572 			if (gVerbose)
   1573 				{
   1574 				printf ("DNGBackwardVersion: %u.%u.%u.%u\n",
   1575 						(unsigned) b0,
   1576 						(unsigned) b1,
   1577 						(unsigned) b2,
   1578 						(unsigned) b3);
   1579 				}
   1580 
   1581 			#endif
   1582 
   1583 			break;
   1584 
   1585 			}
   1586 
   1587 		case tcUniqueCameraModel:
   1588 			{
   1589 
   1590 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
   1591 
   1592 			ParseStringTag (stream,
   1593 							parentCode,
   1594 							tagCode,
   1595 							tagCount,
   1596 							fUniqueCameraModel,
   1597 							false);
   1598 
   1599 			bool didTrim = fUniqueCameraModel.TrimTrailingBlanks ();
   1600 
   1601 			#if qDNGValidate
   1602 
   1603 			if (didTrim)
   1604 				{
   1605 
   1606 				ReportWarning ("UniqueCameraModel string has trailing blanks");
   1607 
   1608 				}
   1609 
   1610 			if (gVerbose)
   1611 				{
   1612 
   1613 				printf ("UniqueCameraModel: ");
   1614 
   1615 				DumpString (fUniqueCameraModel);
   1616 
   1617 				printf ("\n");
   1618 
   1619 				}
   1620 
   1621 			#else
   1622 
   1623 			(void) didTrim;		// Unused
   1624 
   1625 			#endif
   1626 
   1627 			break;
   1628 
   1629 			}
   1630 
   1631 		case tcLocalizedCameraModel:
   1632 			{
   1633 
   1634 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
   1635 
   1636 			ParseStringTag (stream,
   1637 							parentCode,
   1638 							tagCode,
   1639 							tagCount,
   1640 							fLocalizedCameraModel,
   1641 							false);
   1642 
   1643 			bool didTrim = fLocalizedCameraModel.TrimTrailingBlanks ();
   1644 
   1645 			#if qDNGValidate
   1646 
   1647 			if (didTrim)
   1648 				{
   1649 
   1650 				ReportWarning ("LocalizedCameraModel string has trailing blanks");
   1651 
   1652 				}
   1653 
   1654 			if (gVerbose)
   1655 				{
   1656 
   1657 				printf ("LocalizedCameraModel: ");
   1658 
   1659 				DumpString (fLocalizedCameraModel);
   1660 
   1661 				printf ("\n");
   1662 
   1663 				}
   1664 
   1665 			#else
   1666 
   1667 			(void) didTrim;		// Unused
   1668 
   1669 			#endif
   1670 
   1671 			break;
   1672 
   1673 			}
   1674 
   1675 		case tcCameraCalibration1:
   1676 			{
   1677 
   1678 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
   1679 
   1680 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   1681 				return false;
   1682 
   1683 			if (!ParseMatrixTag (stream,
   1684 								 parentCode,
   1685 								 tagCode,
   1686 								 tagType,
   1687 								 tagCount,
   1688 								 fCameraProfile.fColorPlanes,
   1689 								 fCameraProfile.fColorPlanes,
   1690 								 fCameraCalibration1))
   1691 				return false;
   1692 
   1693 			#if qDNGValidate
   1694 
   1695 			if (gVerbose)
   1696 				{
   1697 
   1698 				printf ("CameraCalibration1:\n");
   1699 
   1700 				DumpMatrix (fCameraCalibration1);
   1701 
   1702 				}
   1703 
   1704 			#endif
   1705 
   1706 			break;
   1707 
   1708 			}
   1709 
   1710 		case tcCameraCalibration2:
   1711 			{
   1712 
   1713 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
   1714 
   1715 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   1716 				return false;
   1717 
   1718 			if (!ParseMatrixTag (stream,
   1719 								 parentCode,
   1720 								 tagCode,
   1721 								 tagType,
   1722 								 tagCount,
   1723 								 fCameraProfile.fColorPlanes,
   1724 								 fCameraProfile.fColorPlanes,
   1725 								 fCameraCalibration2))
   1726 				return false;
   1727 
   1728 			#if qDNGValidate
   1729 
   1730 			if (gVerbose)
   1731 				{
   1732 
   1733 				printf ("CameraCalibration2:\n");
   1734 
   1735 				DumpMatrix (fCameraCalibration2);
   1736 
   1737 				}
   1738 
   1739 			#endif
   1740 
   1741 			break;
   1742 
   1743 			}
   1744 
   1745 		case tcCameraCalibrationSignature:
   1746 			{
   1747 
   1748 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
   1749 
   1750 			ParseStringTag (stream,
   1751 							parentCode,
   1752 							tagCode,
   1753 							tagCount,
   1754 							fCameraCalibrationSignature,
   1755 							false);
   1756 
   1757 			#if qDNGValidate
   1758 
   1759 			if (gVerbose)
   1760 				{
   1761 
   1762 				printf ("CameraCalibrationSignature: ");
   1763 
   1764 				DumpString (fCameraCalibrationSignature);
   1765 
   1766 				printf ("\n");
   1767 
   1768 				}
   1769 
   1770 			#endif
   1771 
   1772 			break;
   1773 
   1774 			}
   1775 
   1776 		case tcAnalogBalance:
   1777 			{
   1778 
   1779 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   1780 
   1781 			// Kludge - Hasselblad FFF files are very DNG-like, but sometimes
   1782 			// they don't have any ColorMatrix tags.
   1783 
   1784 			bool hasselbladHack = (fDNGVersion == 0 &&
   1785 								   fCameraProfile.fColorPlanes == 0);
   1786 
   1787 			if (hasselbladHack)
   1788 				{
   1789 
   1790 				fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes);
   1791 
   1792 				#if qDNGValidate
   1793 
   1794 				ReportWarning ("AnalogBalance without ColorMatrix1");
   1795 
   1796 				#endif
   1797 
   1798 				}
   1799 
   1800 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   1801 				return false;
   1802 
   1803 			if (!ParseVectorTag (stream,
   1804 								 parentCode,
   1805 								 tagCode,
   1806 								 tagType,
   1807 								 tagCount,
   1808 								 fCameraProfile.fColorPlanes,
   1809 								 fAnalogBalance))
   1810 				return false;
   1811 
   1812 			#if qDNGValidate
   1813 
   1814 			if (gVerbose)
   1815 				{
   1816 
   1817 				printf ("AnalogBalance:");
   1818 
   1819 				DumpVector (fAnalogBalance);
   1820 
   1821 				}
   1822 
   1823 			#endif
   1824 
   1825 			break;
   1826 
   1827 			}
   1828 
   1829 		case tcAsShotNeutral:
   1830 			{
   1831 
   1832 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   1833 
   1834 			// Kludge - Hasselblad FFF files are very DNG-like, but sometimes
   1835 			// they don't have any ColorMatrix tags.
   1836 
   1837 			bool hasselbladHack = (fDNGVersion == 0 &&
   1838 								   fCameraProfile.fColorPlanes == 0);
   1839 
   1840 			if (hasselbladHack)
   1841 				{
   1842 
   1843 				fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes);
   1844 
   1845 				#if qDNGValidate
   1846 
   1847 				ReportWarning ("AsShotNeutral without ColorMatrix1");
   1848 
   1849 				#endif
   1850 
   1851 				}
   1852 
   1853 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   1854 				return false;
   1855 
   1856 			if (!ParseVectorTag (stream,
   1857 								 parentCode,
   1858 								 tagCode,
   1859 								 tagType,
   1860 								 tagCount,
   1861 								 fCameraProfile.fColorPlanes,
   1862 								 fAsShotNeutral))
   1863 				return false;
   1864 
   1865 			#if qDNGValidate
   1866 
   1867 			if (gVerbose)
   1868 				{
   1869 
   1870 				printf ("AsShotNeutral:");
   1871 
   1872 				DumpVector (fAsShotNeutral);
   1873 
   1874 				}
   1875 
   1876 			#endif
   1877 
   1878 			break;
   1879 
   1880 			}
   1881 
   1882 		case tcAsShotWhiteXY:
   1883 			{
   1884 
   1885 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   1886 
   1887 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   1888 				return false;
   1889 
   1890 			if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
   1891 				return false;
   1892 
   1893 			fAsShotWhiteXY.x = stream.TagValue_real64 (tagType);
   1894 			fAsShotWhiteXY.y = stream.TagValue_real64 (tagType);
   1895 
   1896 			#if qDNGValidate
   1897 
   1898 			if (gVerbose)
   1899 				{
   1900 
   1901 				printf ("AsShotWhiteXY: %0.4f %0.4f\n",
   1902 						fAsShotWhiteXY.x,
   1903 						fAsShotWhiteXY.y);
   1904 
   1905 				}
   1906 
   1907 			#endif
   1908 
   1909 			break;
   1910 
   1911 			}
   1912 
   1913 		case tcBaselineExposure:
   1914 			{
   1915 
   1916 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
   1917 
   1918 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1919 
   1920 			fBaselineExposure = stream.TagValue_srational (tagType);
   1921 
   1922 			fHasBaselineExposure = true;
   1923 
   1924 			#if qDNGValidate
   1925 
   1926 			if (gVerbose)
   1927 				{
   1928 
   1929 				printf ("BaselineExposure: %+0.2f\n",
   1930 					    fBaselineExposure.As_real64 ());
   1931 
   1932 				}
   1933 
   1934 			#endif
   1935 
   1936 			break;
   1937 
   1938 			}
   1939 
   1940 		case tcBaselineNoise:
   1941 			{
   1942 
   1943 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   1944 
   1945 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   1946 
   1947 			fBaselineNoise = stream.TagValue_urational (tagType);
   1948 
   1949 			#if qDNGValidate
   1950 
   1951 			if (gVerbose)
   1952 				{
   1953 
   1954 				printf ("BaselineNoise: %0.2f\n",
   1955 						fBaselineNoise.As_real64 ());
   1956 
   1957 				}
   1958 
   1959 			#endif
   1960 
   1961 			break;
   1962 
   1963 			}
   1964 
   1965 		case tcNoiseReductionApplied:
   1966 			{
   1967 
   1968 			if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
   1969 				return false;
   1970 
   1971 			if (!CheckTagCount (parentCode, tagCode, tagCount, 1))
   1972 				return false;
   1973 
   1974 			fNoiseReductionApplied = stream.TagValue_urational (tagType);
   1975 
   1976 			#if qDNGValidate
   1977 
   1978 			if (gVerbose)
   1979 				{
   1980 
   1981 				printf ("NoiseReductionApplied: %u/%u\n",
   1982 						(unsigned) fNoiseReductionApplied.n,
   1983 						(unsigned) fNoiseReductionApplied.d);
   1984 
   1985 				}
   1986 
   1987 			#endif
   1988 
   1989 			break;
   1990 
   1991 			}
   1992 
   1993 		case tcNoiseProfile:
   1994 			{
   1995 
   1996 			if (!CheckTagType (parentCode, tagCode, tagType, ttDouble))
   1997 				return false;
   1998 
   1999 			// Must be an even, positive number of doubles in a noise profile.
   2000 
   2001 			if (!tagCount || (tagCount & 1))
   2002 				return false;
   2003 
   2004 			// Determine number of planes (i.e., half the number of doubles).
   2005 
   2006 			const uint32 numPlanes = Pin_uint32 (0,
   2007 												 tagCount >> 1,
   2008 												 kMaxColorPlanes);
   2009 
   2010 			// Parse the noise function parameters.
   2011 
   2012 			dng_std_vector<dng_noise_function> noiseFunctions;
   2013 
   2014 			for (uint32 i = 0; i < numPlanes; i++)
   2015 				{
   2016 
   2017 				const real64 scale	= stream.TagValue_real64 (tagType);
   2018 				const real64 offset = stream.TagValue_real64 (tagType);
   2019 
   2020 				noiseFunctions.push_back (dng_noise_function (scale, offset));
   2021 
   2022 				}
   2023 
   2024 			// Store the noise profile.
   2025 
   2026 			fNoiseProfile = dng_noise_profile (noiseFunctions);
   2027 
   2028 			// Debug.
   2029 
   2030 			#if qDNGValidate
   2031 
   2032 			if (gVerbose)
   2033 				{
   2034 
   2035 				printf ("NoiseProfile:\n");
   2036 
   2037 				printf ("  Planes: %u\n", (unsigned) numPlanes);
   2038 
   2039 				for (uint32 plane = 0; plane < numPlanes; plane++)
   2040 					{
   2041 
   2042 					printf ("  Noise function for plane %u: scale = %.8lf, offset = %.8lf\n",
   2043 							(unsigned) plane,
   2044 							noiseFunctions [plane].Scale  (),
   2045 							noiseFunctions [plane].Offset ());
   2046 
   2047 					}
   2048 
   2049 				}
   2050 
   2051 			#endif
   2052 
   2053 			break;
   2054 
   2055 			}
   2056 
   2057 		case tcBaselineSharpness:
   2058 			{
   2059 
   2060 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   2061 
   2062 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2063 
   2064 			fBaselineSharpness = stream.TagValue_urational (tagType);
   2065 
   2066 			#if qDNGValidate
   2067 
   2068 			if (gVerbose)
   2069 				{
   2070 
   2071 				printf ("BaselineSharpness: %0.2f\n",
   2072 					    fBaselineSharpness.As_real64 ());
   2073 
   2074 				}
   2075 
   2076 			#endif
   2077 
   2078 			break;
   2079 
   2080 			}
   2081 
   2082 		case tcLinearResponseLimit:
   2083 			{
   2084 
   2085 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   2086 
   2087 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2088 
   2089 			fLinearResponseLimit = stream.TagValue_urational (tagType);
   2090 
   2091 			#if qDNGValidate
   2092 
   2093 			if (gVerbose)
   2094 				{
   2095 
   2096 				printf ("LinearResponseLimit: %0.2f\n",
   2097 						fLinearResponseLimit.As_real64 ());
   2098 
   2099 				}
   2100 
   2101 			#endif
   2102 
   2103 			break;
   2104 
   2105 			}
   2106 
   2107 		case tcShadowScale:
   2108 			{
   2109 
   2110 			CheckTagType (parentCode, tagCode, tagType, ttRational);
   2111 
   2112 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2113 
   2114 			fShadowScale = stream.TagValue_urational (tagType);
   2115 
   2116 			fHasShadowScale = true;
   2117 
   2118 			#if qDNGValidate
   2119 
   2120 			if (gVerbose)
   2121 				{
   2122 
   2123 				printf ("ShadowScale: %0.4f\n",
   2124 						fShadowScale.As_real64 ());
   2125 
   2126 				}
   2127 
   2128 			#endif
   2129 
   2130 			break;
   2131 
   2132 			}
   2133 
   2134 		case tcDNGPrivateData:
   2135 			{
   2136 
   2137 			CheckTagType (parentCode, tagCode, tagType, ttByte);
   2138 
   2139 			fDNGPrivateDataCount  = tagCount;
   2140 			fDNGPrivateDataOffset = tagOffset;
   2141 
   2142 			#if qDNGValidate
   2143 
   2144 			if (gVerbose)
   2145 				{
   2146 
   2147 				printf ("DNGPrivateData: Count = %u, Offset = %u\n",
   2148 						(unsigned) fDNGPrivateDataCount,
   2149 						(unsigned) fDNGPrivateDataOffset);
   2150 
   2151 				DumpHexAscii (stream, tagCount);
   2152 
   2153 				}
   2154 
   2155 			#endif
   2156 
   2157 			break;
   2158 
   2159 			}
   2160 
   2161 		case tcMakerNoteSafety:
   2162 			{
   2163 
   2164 			CheckTagType (parentCode, tagCode, tagType, ttShort);
   2165 
   2166 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2167 
   2168 			fMakerNoteSafety = stream.TagValue_uint32 (tagType);
   2169 
   2170 			#if qDNGValidate
   2171 
   2172 			if (gVerbose)
   2173 				{
   2174 
   2175 				printf ("MakerNoteSafety: %s\n",
   2176 						LookupMakerNoteSafety (fMakerNoteSafety));
   2177 
   2178 				}
   2179 
   2180 			#endif
   2181 
   2182 			break;
   2183 
   2184 			}
   2185 
   2186 		case tcRawImageDigest:
   2187 			{
   2188 
   2189 			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
   2190 				return false;
   2191 
   2192 			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
   2193 				return false;
   2194 
   2195 			stream.Get (fRawImageDigest.data, 16);
   2196 
   2197 			#if qDNGValidate
   2198 
   2199 			if (gVerbose)
   2200 				{
   2201 
   2202 				printf ("RawImageDigest: ");
   2203 
   2204 				DumpFingerprint (fRawImageDigest);
   2205 
   2206 				printf ("\n");
   2207 
   2208 				}
   2209 
   2210 			#endif
   2211 
   2212 			break;
   2213 
   2214 			}
   2215 
   2216 		case tcNewRawImageDigest:
   2217 			{
   2218 
   2219 			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
   2220 				return false;
   2221 
   2222 			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
   2223 				return false;
   2224 
   2225 			stream.Get (fNewRawImageDigest.data, 16);
   2226 
   2227 			#if qDNGValidate
   2228 
   2229 			if (gVerbose)
   2230 				{
   2231 
   2232 				printf ("NewRawImageDigest: ");
   2233 
   2234 				DumpFingerprint (fNewRawImageDigest);
   2235 
   2236 				printf ("\n");
   2237 
   2238 				}
   2239 
   2240 			#endif
   2241 
   2242 			break;
   2243 
   2244 			}
   2245 
   2246 		case tcRawDataUniqueID:
   2247 			{
   2248 
   2249 			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
   2250 				return false;
   2251 
   2252 			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
   2253 				return false;
   2254 
   2255 			stream.Get (fRawDataUniqueID.data, 16);
   2256 
   2257 			#if qDNGValidate
   2258 
   2259 			if (gVerbose)
   2260 				{
   2261 
   2262 				printf ("RawDataUniqueID: ");
   2263 
   2264 				DumpFingerprint (fRawDataUniqueID);
   2265 
   2266 				printf ("\n");
   2267 
   2268 				}
   2269 
   2270 			#endif
   2271 
   2272 			break;
   2273 
   2274 			}
   2275 
   2276 		case tcOriginalRawFileName:
   2277 			{
   2278 
   2279 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
   2280 
   2281 			ParseStringTag (stream,
   2282 							parentCode,
   2283 							tagCode,
   2284 							tagCount,
   2285 							fOriginalRawFileName,
   2286 							false);
   2287 
   2288 			#if qDNGValidate
   2289 
   2290 			if (gVerbose)
   2291 				{
   2292 
   2293 				printf ("OriginalRawFileName: ");
   2294 
   2295 				DumpString (fOriginalRawFileName);
   2296 
   2297 				printf ("\n");
   2298 
   2299 				}
   2300 
   2301 			#endif
   2302 
   2303 			break;
   2304 
   2305 			}
   2306 
   2307 		case tcOriginalRawFileData:
   2308 			{
   2309 
   2310 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
   2311 
   2312 			fOriginalRawFileDataCount  = tagCount;
   2313 			fOriginalRawFileDataOffset = tagOffset;
   2314 
   2315 			#if qDNGValidate
   2316 
   2317 			if (gVerbose)
   2318 				{
   2319 
   2320 				printf ("OriginalRawFileData: Count = %u, Offset = %u\n",
   2321 						(unsigned) fOriginalRawFileDataCount,
   2322 						(unsigned) fOriginalRawFileDataOffset);
   2323 
   2324 				DumpHexAscii (stream, tagCount);
   2325 
   2326 				}
   2327 
   2328 			#endif
   2329 
   2330 			break;
   2331 
   2332 			}
   2333 
   2334 		case tcOriginalRawFileDigest:
   2335 			{
   2336 
   2337 			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
   2338 				return false;
   2339 
   2340 			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
   2341 				return false;
   2342 
   2343 			stream.Get (fOriginalRawFileDigest.data, 16);
   2344 
   2345 			#if qDNGValidate
   2346 
   2347 			if (gVerbose)
   2348 				{
   2349 
   2350 				printf ("OriginalRawFileDigest: ");
   2351 
   2352 				DumpFingerprint (fOriginalRawFileDigest);
   2353 
   2354 				printf ("\n");
   2355 
   2356 				}
   2357 
   2358 			#endif
   2359 
   2360 			break;
   2361 
   2362 			}
   2363 
   2364 		case tcAsShotICCProfile:
   2365 			{
   2366 
   2367 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
   2368 
   2369 			fAsShotICCProfileCount  = tagCount;
   2370 			fAsShotICCProfileOffset = tagOffset;
   2371 
   2372 			#if qDNGValidate
   2373 
   2374 			if (gVerbose)
   2375 				{
   2376 
   2377 				printf ("AsShotICCProfile: Count = %u, Offset = %u\n",
   2378 						(unsigned) fAsShotICCProfileCount,
   2379 						(unsigned) fAsShotICCProfileOffset);
   2380 
   2381 				DumpHexAscii (stream, tagCount);
   2382 
   2383 				}
   2384 
   2385 			#endif
   2386 
   2387 			break;
   2388 
   2389 			}
   2390 
   2391 		case tcAsShotPreProfileMatrix:
   2392 			{
   2393 
   2394 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
   2395 
   2396 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   2397 				return false;
   2398 
   2399 			uint32 rows = fCameraProfile.fColorPlanes;
   2400 
   2401 			if (tagCount == fCameraProfile.fColorPlanes * 3)
   2402 				{
   2403 				rows = 3;
   2404 				}
   2405 
   2406 			if (!ParseMatrixTag (stream,
   2407 								 parentCode,
   2408 								 tagCode,
   2409 								 tagType,
   2410 								 tagCount,
   2411 								 rows,
   2412 								 fCameraProfile.fColorPlanes,
   2413 								 fAsShotPreProfileMatrix))
   2414 				return false;
   2415 
   2416 			#if qDNGValidate
   2417 
   2418 			if (gVerbose)
   2419 				{
   2420 
   2421 				printf ("AsShotPreProfileMatrix:\n");
   2422 
   2423 				DumpMatrix (fAsShotPreProfileMatrix);
   2424 
   2425 				}
   2426 
   2427 			#endif
   2428 
   2429 			break;
   2430 
   2431 			}
   2432 
   2433 		case tcCurrentICCProfile:
   2434 			{
   2435 
   2436 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
   2437 
   2438 			fCurrentICCProfileCount  = tagCount;
   2439 			fCurrentICCProfileOffset = tagOffset;
   2440 
   2441 			#if qDNGValidate
   2442 
   2443 			if (gVerbose)
   2444 				{
   2445 
   2446 				printf ("CurrentICCProfile: Count = %u, Offset = %u\n",
   2447 						(unsigned) fCurrentICCProfileCount,
   2448 						(unsigned) fCurrentICCProfileOffset);
   2449 
   2450 				DumpHexAscii (stream, tagCount);
   2451 
   2452 				}
   2453 
   2454 			#endif
   2455 
   2456 			break;
   2457 
   2458 			}
   2459 
   2460 		case tcCurrentPreProfileMatrix:
   2461 			{
   2462 
   2463 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
   2464 
   2465 			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
   2466 				return false;
   2467 
   2468 			uint32 rows = fCameraProfile.fColorPlanes;
   2469 
   2470 			if (tagCount == fCameraProfile.fColorPlanes * 3)
   2471 				{
   2472 				rows = 3;
   2473 				}
   2474 
   2475 			if (!ParseMatrixTag (stream,
   2476 								 parentCode,
   2477 								 tagCode,
   2478 								 tagType,
   2479 								 tagCount,
   2480 								 rows,
   2481 								 fCameraProfile.fColorPlanes,
   2482 								 fCurrentPreProfileMatrix))
   2483 				return false;
   2484 
   2485 			#if qDNGValidate
   2486 
   2487 			if (gVerbose)
   2488 				{
   2489 
   2490 				printf ("CurrentPreProfileMatrix:\n");
   2491 
   2492 				DumpMatrix (fCurrentPreProfileMatrix);
   2493 
   2494 				}
   2495 
   2496 			#endif
   2497 
   2498 			break;
   2499 
   2500 			}
   2501 
   2502 		case tcColorimetricReference:
   2503 			{
   2504 
   2505 			CheckTagType (parentCode, tagCode, tagType, ttShort);
   2506 
   2507 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2508 
   2509 			fColorimetricReference = stream.TagValue_uint32 (tagType);
   2510 
   2511 			#if qDNGValidate
   2512 
   2513 			if (gVerbose)
   2514 				{
   2515 
   2516 				printf ("ColorimetricReference: %s\n",
   2517 						LookupColorimetricReference (fColorimetricReference));
   2518 
   2519 				}
   2520 
   2521 			#endif
   2522 
   2523 			break;
   2524 
   2525 			}
   2526 
   2527 		case tcExtraCameraProfiles:
   2528 			{
   2529 
   2530 			CheckTagType (parentCode, tagCode, tagType, ttLong);
   2531 
   2532 			CheckTagCount (parentCode, tagCode, tagCount, 1, tagCount);
   2533 
   2534 			#if qDNGValidate
   2535 
   2536 			if (gVerbose)
   2537 				{
   2538 
   2539 				printf ("ExtraCameraProfiles: %u\n", (unsigned) tagCount);
   2540 
   2541 				}
   2542 
   2543 			#endif
   2544 
   2545 			fExtraCameraProfiles.reserve (tagCount);
   2546 
   2547 			for (uint32 index = 0; index < tagCount; index++)
   2548 				{
   2549 
   2550 				#if qDNGValidate
   2551 
   2552 				if (gVerbose)
   2553 					{
   2554 
   2555 					printf ("\nExtraCameraProfile [%u]:\n\n", (unsigned) index);
   2556 
   2557 					}
   2558 
   2559 				#endif
   2560 
   2561 				stream.SetReadPosition (tagOffset + index * 4);
   2562 
   2563 				uint32 profileOffset = stream.TagValue_uint32 (tagType);
   2564 
   2565 				dng_camera_profile_info profileInfo;
   2566 
   2567 				stream.SetReadPosition (profileOffset);
   2568 
   2569 				if (profileInfo.ParseExtended (stream))
   2570 					{
   2571 
   2572 					fExtraCameraProfiles.push_back (profileInfo);
   2573 
   2574 					}
   2575 
   2576 				else
   2577 					{
   2578 
   2579 					#if qDNGValidate
   2580 
   2581 					ReportWarning ("Unable to parse extra camera profile");
   2582 
   2583 					#endif
   2584 
   2585 					}
   2586 
   2587 				}
   2588 
   2589 			#if qDNGValidate
   2590 
   2591 			if (gVerbose)
   2592 				{
   2593 
   2594 				printf ("\nDone with ExtraCameraProfiles\n\n");
   2595 
   2596 				}
   2597 
   2598 			#endif
   2599 
   2600 			break;
   2601 
   2602 			}
   2603 
   2604 		case tcAsShotProfileName:
   2605 			{
   2606 
   2607 			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
   2608 
   2609 			ParseStringTag (stream,
   2610 							parentCode,
   2611 							tagCode,
   2612 							tagCount,
   2613 							fAsShotProfileName,
   2614 							false);
   2615 
   2616 			#if qDNGValidate
   2617 
   2618 			if (gVerbose)
   2619 				{
   2620 
   2621 				printf ("AsShotProfileName: ");
   2622 
   2623 				DumpString (fAsShotProfileName);
   2624 
   2625 				printf ("\n");
   2626 
   2627 				}
   2628 
   2629 			#endif
   2630 
   2631 			break;
   2632 
   2633 			}
   2634 
   2635 		case tcOriginalDefaultFinalSize:
   2636 			{
   2637 
   2638 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
   2639 
   2640 			if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
   2641 				return false;
   2642 
   2643 			fOriginalDefaultFinalSize.h = stream.TagValue_int32 (tagType);
   2644 			fOriginalDefaultFinalSize.v = stream.TagValue_int32 (tagType);
   2645 
   2646 			#if qDNGValidate
   2647 
   2648 			if (gVerbose)
   2649 				{
   2650 
   2651 				printf ("OriginalDefaultFinalSize: H = %d V = %d\n",
   2652 						(int) fOriginalDefaultFinalSize.h,
   2653 						(int) fOriginalDefaultFinalSize.v);
   2654 
   2655 				}
   2656 
   2657 			#endif
   2658 
   2659 			break;
   2660 
   2661 			}
   2662 
   2663 		case tcOriginalBestQualityFinalSize:
   2664 			{
   2665 
   2666 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
   2667 
   2668 			if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
   2669 				return false;
   2670 
   2671 			fOriginalBestQualityFinalSize.h = stream.TagValue_int32 (tagType);
   2672 			fOriginalBestQualityFinalSize.v = stream.TagValue_int32 (tagType);
   2673 
   2674 			#if qDNGValidate
   2675 
   2676 			if (gVerbose)
   2677 				{
   2678 
   2679 				printf ("OriginalBestQualityFinalSize: H = %d V = %d\n",
   2680 						(int) fOriginalBestQualityFinalSize.h,
   2681 						(int) fOriginalBestQualityFinalSize.v);
   2682 
   2683 				}
   2684 
   2685 			#endif
   2686 
   2687 			break;
   2688 
   2689 			}
   2690 
   2691 		case tcOriginalDefaultCropSize:
   2692 			{
   2693 
   2694 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational);
   2695 
   2696 			if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
   2697 				return false;
   2698 
   2699 			fOriginalDefaultCropSizeH = stream.TagValue_urational (tagType);
   2700 			fOriginalDefaultCropSizeV = stream.TagValue_urational (tagType);
   2701 
   2702 			#if qDNGValidate
   2703 
   2704 			if (gVerbose)
   2705 				{
   2706 
   2707 				printf ("OriginalDefaultCropSize: H = %0.2f V = %0.2f\n",
   2708 						fOriginalDefaultCropSizeH.As_real64 (),
   2709 						fOriginalDefaultCropSizeV.As_real64 ());
   2710 
   2711 				}
   2712 
   2713 			#endif
   2714 
   2715 			break;
   2716 
   2717 			}
   2718 
   2719 		default:
   2720 			{
   2721 
   2722 			// The main camera profile tags also appear in IFD 0
   2723 
   2724 			return fCameraProfile.ParseTag (stream,
   2725 											parentCode,
   2726 											tagCode,
   2727 											tagType,
   2728 											tagCount,
   2729 											tagOffset);
   2730 
   2731 			}
   2732 
   2733 		}
   2734 
   2735 	return true;
   2736 
   2737 	}
   2738 
   2739 /*****************************************************************************/
   2740 
   2741 // Parses tags that should only appear in IFD 0 or EXIF IFD.
   2742 
   2743 bool dng_shared::Parse_ifd0_exif (dng_stream &stream,
   2744 								  dng_exif & /* exif */,
   2745 						  	   	  uint32 parentCode,
   2746 						  	      uint32 tagCode,
   2747 						  	      uint32 tagType,
   2748 						  	      uint32 tagCount,
   2749 						  	      uint64 tagOffset)
   2750 	{
   2751 
   2752 	switch (tagCode)
   2753 		{
   2754 
   2755 		case tcMakerNote:
   2756 			{
   2757 
   2758 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
   2759 
   2760 			fMakerNoteCount  = tagCount;
   2761 			fMakerNoteOffset = tagOffset;
   2762 
   2763 			#if qDNGValidate
   2764 
   2765 			if (gVerbose)
   2766 				{
   2767 
   2768 				printf ("MakerNote: Count = %u, Offset = %u\n",
   2769 						(unsigned) fMakerNoteCount,
   2770 						(unsigned) fMakerNoteOffset);
   2771 
   2772 				DumpHexAscii (stream, tagCount);
   2773 
   2774 				}
   2775 
   2776 			#endif
   2777 
   2778 			break;
   2779 
   2780 			}
   2781 
   2782 		case tcInteroperabilityIFD:
   2783 			{
   2784 
   2785 			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
   2786 
   2787 			CheckTagCount (parentCode, tagCode, tagCount, 1);
   2788 
   2789 			fInteroperabilityIFD = stream.TagValue_uint32 (tagType);
   2790 
   2791 			#if qDNGValidate
   2792 
   2793 			if (gVerbose)
   2794 				{
   2795 				printf ("InteroperabilityIFD: %u\n", (unsigned) fInteroperabilityIFD);
   2796 				}
   2797 
   2798 			#endif
   2799 
   2800 			break;
   2801 
   2802 			}
   2803 
   2804 		default:
   2805 			{
   2806 
   2807 			return false;
   2808 
   2809 			}
   2810 
   2811 		}
   2812 
   2813 	return true;
   2814 
   2815 	}
   2816 
   2817 /*****************************************************************************/
   2818 
   2819 void dng_shared::PostParse (dng_host & /* host */,
   2820 							dng_exif & /* exif */)
   2821 	{
   2822 
   2823 	// Fill in default values for DNG images.
   2824 
   2825 	if (fDNGVersion != 0)
   2826 		{
   2827 
   2828 		// Support for DNG versions before 1.0.0.0.
   2829 
   2830 		if (fDNGVersion < dngVersion_1_0_0_0)
   2831 			{
   2832 
   2833 			#if qDNGValidate
   2834 
   2835 			ReportWarning ("DNGVersion less than 1.0.0.0");
   2836 
   2837 			#endif
   2838 
   2839 			// The CalibrationIlluminant tags were added just before
   2840 			// DNG version 1.0.0.0, and were hardcoded before that.
   2841 
   2842 			fCameraProfile.fCalibrationIlluminant1 = lsStandardLightA;
   2843 			fCameraProfile.fCalibrationIlluminant2 = lsD65;
   2844 
   2845 			fDNGVersion = dngVersion_1_0_0_0;
   2846 
   2847 			}
   2848 
   2849 		// Default value for DNGBackwardVersion tag.
   2850 
   2851 		if (fDNGBackwardVersion == 0)
   2852 			{
   2853 
   2854 			fDNGBackwardVersion = fDNGVersion & 0xFFFF0000;
   2855 
   2856 			}
   2857 
   2858 		// Check DNGBackwardVersion value.
   2859 
   2860 		if (fDNGBackwardVersion < dngVersion_1_0_0_0)
   2861 			{
   2862 
   2863 			#if qDNGValidate
   2864 
   2865 			ReportWarning ("DNGBackwardVersion less than 1.0.0.0");
   2866 
   2867 			#endif
   2868 
   2869 			fDNGBackwardVersion = dngVersion_1_0_0_0;
   2870 
   2871 			}
   2872 
   2873 		if (fDNGBackwardVersion > fDNGVersion)
   2874 			{
   2875 
   2876 			#if qDNGValidate
   2877 
   2878 			ReportWarning ("DNGBackwardVersion > DNGVersion");
   2879 
   2880 			#endif
   2881 
   2882 			fDNGBackwardVersion = fDNGVersion;
   2883 
   2884 			}
   2885 
   2886 		// Check UniqueCameraModel.
   2887 
   2888 		if (fUniqueCameraModel.IsEmpty ())
   2889 			{
   2890 
   2891 			#if qDNGValidate
   2892 
   2893 			ReportWarning ("Missing or invalid UniqueCameraModel");
   2894 
   2895 			#endif
   2896 
   2897 			fUniqueCameraModel.Set ("Digital Negative");
   2898 
   2899 			}
   2900 
   2901 		// If we don't know the color depth yet, it must be a monochrome DNG.
   2902 
   2903 		if (fCameraProfile.fColorPlanes == 0)
   2904 			{
   2905 
   2906 			fCameraProfile.fColorPlanes = 1;
   2907 
   2908 			}
   2909 
   2910 		// Check color info.
   2911 
   2912 		if (fCameraProfile.fColorPlanes > 1)
   2913 			{
   2914 
   2915 			// Check illuminant pair.
   2916 
   2917 			if (fCameraProfile.fColorMatrix2.NotEmpty ())
   2918 				{
   2919 
   2920 				if (fCameraProfile.fCalibrationIlluminant1 == lsUnknown ||
   2921 					(fCameraProfile.fCalibrationIlluminant2 == lsUnknown ||
   2922 					(fCameraProfile.fCalibrationIlluminant1 == fCameraProfile.fCalibrationIlluminant2)))
   2923 					{
   2924 
   2925 					#if qDNGValidate
   2926 
   2927 					ReportWarning ("Invalid CalibrationIlluminant pair");
   2928 
   2929 					#endif
   2930 
   2931 					fCameraProfile.fColorMatrix2 = dng_matrix ();
   2932 
   2933 					}
   2934 
   2935 				}
   2936 
   2937 			// If the colorimetric reference is the ICC profile PCS, then the
   2938 			// data must already be white balanced.  The "AsShotWhiteXY" is required
   2939 			// to be the ICC Profile PCS white point.
   2940 
   2941 			if (fColorimetricReference == crICCProfilePCS)
   2942 				{
   2943 
   2944 				if (fAsShotNeutral.NotEmpty ())
   2945 					{
   2946 
   2947 					#if qDNGValidate
   2948 
   2949 					ReportWarning ("AsShotNeutral not allowed for this "
   2950 								   "ColorimetricReference value");
   2951 
   2952 					#endif
   2953 
   2954 					fAsShotNeutral.Clear ();
   2955 
   2956 					}
   2957 
   2958 				dng_xy_coord pcs = PCStoXY ();
   2959 
   2960 				#if qDNGValidate
   2961 
   2962 				if (fAsShotWhiteXY.IsValid ())
   2963 					{
   2964 
   2965 					if (Abs_real64 (fAsShotWhiteXY.x - pcs.x) > 0.01 ||
   2966 						Abs_real64 (fAsShotWhiteXY.y - pcs.y) > 0.01)
   2967 						{
   2968 
   2969 						ReportWarning ("AsShotWhiteXY does not match the ICC Profile PCS");
   2970 
   2971 						}
   2972 
   2973 					}
   2974 
   2975 				#endif
   2976 
   2977 				fAsShotWhiteXY = pcs;
   2978 
   2979 				}
   2980 
   2981 			else
   2982 				{
   2983 
   2984 				// Warn if both AsShotNeutral and AsShotWhiteXY are specified.
   2985 
   2986 				if (fAsShotNeutral.NotEmpty () && fAsShotWhiteXY.IsValid ())
   2987 					{
   2988 
   2989 					#if qDNGValidate
   2990 
   2991 					ReportWarning ("Both AsShotNeutral and AsShotWhiteXY included");
   2992 
   2993 					#endif
   2994 
   2995 					fAsShotWhiteXY = dng_xy_coord ();
   2996 
   2997 					}
   2998 
   2999 				// Warn if neither AsShotNeutral nor AsShotWhiteXY are specified.
   3000 
   3001 				#if qDNGValidate
   3002 
   3003 				if (fAsShotNeutral.IsEmpty () && !fAsShotWhiteXY.IsValid ())
   3004 					{
   3005 
   3006 					ReportWarning ("Neither AsShotNeutral nor AsShotWhiteXY included",
   3007 								   "legal but not recommended");
   3008 
   3009 					}
   3010 
   3011 				#endif
   3012 
   3013 				}
   3014 
   3015 			// Default values of calibration signatures are required for legacy
   3016 			// compatiblity.
   3017 
   3018 			if (fCameraProfile.fCalibrationIlluminant1 == lsStandardLightA &&
   3019 				fCameraProfile.fCalibrationIlluminant2 == lsD65            &&
   3020 				fCameraCalibration1.Rows () == fCameraProfile.fColorPlanes &&
   3021 				fCameraCalibration1.Cols () == fCameraProfile.fColorPlanes &&
   3022 				fCameraCalibration2.Rows () == fCameraProfile.fColorPlanes &&
   3023 				fCameraCalibration2.Cols () == fCameraProfile.fColorPlanes &&
   3024 				fCameraCalibrationSignature.IsEmpty ()                     &&
   3025 				fCameraProfile.fProfileCalibrationSignature.IsEmpty ()     )
   3026 				{
   3027 
   3028 				fCameraCalibrationSignature.Set (kAdobeCalibrationSignature);
   3029 
   3030 				fCameraProfile.fProfileCalibrationSignature.Set (kAdobeCalibrationSignature);
   3031 
   3032 				}
   3033 
   3034 			}
   3035 
   3036 		// Check BaselineNoise.
   3037 
   3038 		if (fBaselineNoise.As_real64 () <= 0.0)
   3039 			{
   3040 
   3041 			#if qDNGValidate
   3042 
   3043 			ReportWarning ("Invalid BaselineNoise");
   3044 
   3045 			#endif
   3046 
   3047 			fBaselineNoise = dng_urational (1, 1);
   3048 
   3049 			}
   3050 
   3051 		// Check BaselineSharpness.
   3052 
   3053 		if (fBaselineSharpness.As_real64 () <= 0.0)
   3054 			{
   3055 
   3056 			#if qDNGValidate
   3057 
   3058 			ReportWarning ("Invalid BaselineSharpness");
   3059 
   3060 			#endif
   3061 
   3062 			fBaselineSharpness = dng_urational (1, 1);
   3063 
   3064 			}
   3065 
   3066 		// Check NoiseProfile.
   3067 
   3068 		if (!fNoiseProfile.IsValid () && fNoiseProfile.NumFunctions () != 0)
   3069 			{
   3070 
   3071 			#if qDNGValidate
   3072 
   3073 			ReportWarning ("Invalid NoiseProfile");
   3074 
   3075 			#endif
   3076 
   3077 			fNoiseProfile = dng_noise_profile ();
   3078 
   3079 			}
   3080 
   3081 		// Check LinearResponseLimit.
   3082 
   3083 		if (fLinearResponseLimit.As_real64 () < 0.5 ||
   3084 			fLinearResponseLimit.As_real64 () > 1.0)
   3085 			{
   3086 
   3087 			#if qDNGValidate
   3088 
   3089 			ReportWarning ("Invalid LinearResponseLimit");
   3090 
   3091 			#endif
   3092 
   3093 			fLinearResponseLimit = dng_urational (1, 1);
   3094 
   3095 			}
   3096 
   3097 		// Check ShadowScale.
   3098 
   3099 		if (fShadowScale.As_real64 () <= 0.0)
   3100 			{
   3101 
   3102 			#if qDNGValidate
   3103 
   3104 			ReportWarning ("Invalid ShadowScale");
   3105 
   3106 			#endif
   3107 
   3108 			fShadowScale = dng_urational (1, 1);
   3109 
   3110 			}
   3111 
   3112 		}
   3113 
   3114 	}
   3115 
   3116 /*****************************************************************************/
   3117 
   3118 bool dng_shared::IsValidDNG ()
   3119 	{
   3120 
   3121 	// Check DNGVersion value.
   3122 
   3123 	if (fDNGVersion < dngVersion_1_0_0_0)
   3124 		{
   3125 
   3126 		#if qDNGValidate
   3127 
   3128 		if (fDNGVersion != dngVersion_None)
   3129 			{
   3130 
   3131 			ReportError ("Invalid DNGVersion");
   3132 
   3133 			}
   3134 
   3135 		#if qDNGValidateTarget
   3136 
   3137 		else
   3138 			{
   3139 
   3140 			ReportError ("Missing DNGVersion");
   3141 
   3142 			}
   3143 
   3144 		#endif
   3145 
   3146 		#endif
   3147 
   3148 		return false;
   3149 
   3150 		}
   3151 
   3152 	// Check DNGBackwardVersion value.
   3153 
   3154 	if (fDNGBackwardVersion > dngVersion_Current)
   3155 		{
   3156 
   3157 		#if qDNGValidate
   3158 
   3159 		ReportError ("DNGBackwardVersion (or DNGVersion) is too high");
   3160 
   3161 		#endif
   3162 
   3163 		ThrowUnsupportedDNG ();
   3164 
   3165 		}
   3166 
   3167 	// Check color transform info.
   3168 
   3169 	if (fCameraProfile.fColorPlanes > 1)
   3170 		{
   3171 
   3172 		// CameraCalibration1 is optional, but it must be valid if present.
   3173 
   3174 		if (fCameraCalibration1.Cols () != 0 ||
   3175 			fCameraCalibration1.Rows () != 0)
   3176 			{
   3177 
   3178 			if (fCameraCalibration1.Cols () != fCameraProfile.fColorPlanes ||
   3179 				fCameraCalibration1.Rows () != fCameraProfile.fColorPlanes)
   3180 				{
   3181 
   3182 				#if qDNGValidate
   3183 
   3184 				ReportError ("CameraCalibration1 is wrong size");
   3185 
   3186 				#endif
   3187 
   3188 				return false;
   3189 
   3190 				}
   3191 
   3192 			// Make sure it is invertable.
   3193 
   3194 			try
   3195 				{
   3196 
   3197 				(void) Invert (fCameraCalibration1);
   3198 
   3199 				}
   3200 
   3201 			catch (...)
   3202 				{
   3203 
   3204 				#if qDNGValidate
   3205 
   3206 				ReportError ("CameraCalibration1 is not invertable");
   3207 
   3208 				#endif
   3209 
   3210 				return false;
   3211 
   3212 				}
   3213 
   3214 			}
   3215 
   3216 		// CameraCalibration2 is optional, but it must be valid if present.
   3217 
   3218 		if (fCameraCalibration2.Cols () != 0 ||
   3219 			fCameraCalibration2.Rows () != 0)
   3220 			{
   3221 
   3222 			if (fCameraCalibration2.Cols () != fCameraProfile.fColorPlanes ||
   3223 				fCameraCalibration2.Rows () != fCameraProfile.fColorPlanes)
   3224 				{
   3225 
   3226 				#if qDNGValidate
   3227 
   3228 				ReportError ("CameraCalibration2 is wrong size");
   3229 
   3230 				#endif
   3231 
   3232 				return false;
   3233 
   3234 				}
   3235 
   3236 			// Make sure it is invertable.
   3237 
   3238 			try
   3239 				{
   3240 
   3241 				(void) Invert (fCameraCalibration2);
   3242 
   3243 				}
   3244 
   3245 			catch (...)
   3246 				{
   3247 
   3248 				#if qDNGValidate
   3249 
   3250 				ReportError ("CameraCalibration2 is not invertable");
   3251 
   3252 				#endif
   3253 
   3254 				return false;
   3255 
   3256 				}
   3257 
   3258 			}
   3259 
   3260 		// Check analog balance
   3261 
   3262 		dng_matrix analogBalance;
   3263 
   3264 		if (fAnalogBalance.NotEmpty ())
   3265 			{
   3266 
   3267 			analogBalance = fAnalogBalance.AsDiagonal ();
   3268 
   3269 			try
   3270 				{
   3271 
   3272 				(void) Invert (analogBalance);
   3273 
   3274 				}
   3275 
   3276 			catch (...)
   3277 				{
   3278 
   3279 				#if qDNGValidate
   3280 
   3281 				ReportError ("AnalogBalance is not invertable");
   3282 
   3283 				#endif
   3284 
   3285 				return false;
   3286 
   3287 				}
   3288 
   3289 			}
   3290 
   3291 		}
   3292 
   3293 	return true;
   3294 
   3295 	}
   3296 
   3297 /*****************************************************************************/
   3298