Exemple #1
0
dng_basic_tag_set * dng_image_preview::AddTagSet (dng_tiff_directory &directory) const
	{
	
	fIFD.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage
											: sfAltPreviewImage;
	
	fIFD.fImageWidth  = fImage->Width  ();
	fIFD.fImageLength = fImage->Height ();
	
	fIFD.fSamplesPerPixel = fImage->Planes ();
	
	fIFD.fPhotometricInterpretation = fIFD.fSamplesPerPixel == 1 ? piBlackIsZero
																 : piRGB;
																 
	fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
	
	for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++)
		{
		fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0];
		}
		
	fIFD.SetSingleStrip ();
	
	return new dng_preview_tag_set (directory, *this, fIFD);
	
	}
void dng_filter_task::Start (uint32 threadCount,
							 const dng_point &tileSize,
							 dng_memory_allocator *allocator,
							 dng_abort_sniffer * /* sniffer */)
	{
	
	dng_point srcTileSize = SrcTileSize (tileSize);
	
	uint32 srcPixelSize = TagTypeSize (fSrcPixelType);
						   
	uint32 srcBufferSize = srcTileSize.v *
						   RoundUpForPixelSize (srcTileSize.h, srcPixelSize) *
						   srcPixelSize *
						   fSrcPlanes;
						   
	uint32 dstPixelSize = TagTypeSize (fDstPixelType);
						   
	uint32 dstBufferSize = tileSize.v *
						   RoundUpForPixelSize (tileSize.h, dstPixelSize) *
						   dstPixelSize *
						   fDstPlanes;
						   
	for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
		{
		
		fSrcBuffer [threadIndex] . Reset (allocator->Allocate (srcBufferSize));
		
		fDstBuffer [threadIndex] . Reset (allocator->Allocate (dstBufferSize));
		
		// Zero buffers so add pad bytes have defined values.
		
		DoZeroBytes (fSrcBuffer [threadIndex]->Buffer      (),
					 fSrcBuffer [threadIndex]->LogicalSize ());
		
		DoZeroBytes (fDstBuffer [threadIndex]->Buffer      (),
					 fDstBuffer [threadIndex]->LogicalSize ());
		
		}
		
	}
Exemple #3
0
dng_basic_tag_set * dng_raw_preview::AddTagSet (dng_tiff_directory &directory) const
	{
	
	fIFD.fNewSubFileType = sfPreviewImage;
	
	fIFD.fImageWidth  = fImage->Width  ();
	fIFD.fImageLength = fImage->Height ();
	
	fIFD.fSamplesPerPixel = fImage->Planes ();
	
	fIFD.fPhotometricInterpretation = piLinearRaw;
	
	if (fImage->PixelType () == ttFloat)
		{
		
		fIFD.fCompression = ccDeflate;
		
		fIFD.fCompressionQuality = fCompressionQuality;

		fIFD.fPredictor = cpFloatingPoint;
		
		for (uint32 j = 0; j < fIFD.fSamplesPerPixel; j++)
			{
			fIFD.fBitsPerSample [j] = 16;
			fIFD.fSampleFormat  [j] = sfFloatingPoint;
			}
			
		fIFD.FindTileSize (512 * 1024);
		
		}
		
	else
		{
	
		fIFD.fCompression = ccLossyJPEG;
		
		fIFD.fCompressionQuality = fCompressionQuality;
																	 
		fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
		
		for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++)
			{
			fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0];
			}
			
		fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel);
		
		}
	
	return new dng_raw_preview_tag_set (directory, *this, fIFD);
	
	}
void dng_image::SetPixelType (uint32 pixelType)
	{
	
	if (TagTypeSize (pixelType) != PixelSize ())
		{
		
		ThrowProgramError ("Cannot change pixel size for existing image");
		
		}
	
	fPixelType = pixelType;
	
	}
dng_simple_image::dng_simple_image (const dng_rect &bounds,
									uint32 planes,
								    uint32 pixelType,
								    uint32 pixelRange,
								    dng_memory_allocator &allocator)
								    
	:	dng_image (bounds,
				   planes,
				   pixelType,
				   pixelRange)
				   
	,	fMemory ()
	,	fBuffer ()
				   
	{
	
	uint32 pixelSize = TagTypeSize (pixelType);
	
	uint32 bytes = bounds.H () * bounds.W () * planes * pixelSize;
				   
	fMemory.Reset (allocator.Allocate (bytes));
	
	fBuffer.fArea = bounds;
	
	fBuffer.fPlane  = 0;
	fBuffer.fPlanes = planes;
	
	fBuffer.fRowStep   = planes * bounds.W ();
	fBuffer.fColStep   = planes;
	fBuffer.fPlaneStep = 1;
	
	fBuffer.fPixelType  = pixelType;
	fBuffer.fPixelSize  = pixelSize;
	fBuffer.fPixelRange = pixelRange;
	
	fBuffer.fData = fMemory->Buffer ();
	
	}
uint32 ComputeBufferSize(uint32 pixelType, const dng_point &tileSize,
						 uint32 numPlanes, PaddingType paddingType)

{
	
	// Convert tile size to uint32.
	if (tileSize.h < 0 || tileSize.v < 0)
		{
		ThrowMemoryFull("Negative tile size");
		}
	const uint32 tileSizeH = static_cast<uint32>(tileSize.h);
	const uint32 tileSizeV = static_cast<uint32>(tileSize.v);
	
	const uint32 pixelSize = TagTypeSize(pixelType);
	
	// Add padding to width if necessary.
	uint32 paddedWidth = tileSizeH;
	if (paddingType == pad16Bytes)
		{
		if (!RoundUpForPixelSize(paddedWidth, pixelSize, &paddedWidth))
			{
			  ThrowMemoryFull("Arithmetic overflow computing buffer size");
			}
		}
	
	// Compute buffer size.
	uint32 bufferSize;
	if (!SafeUint32Mult(paddedWidth, tileSizeV, &bufferSize) ||
		!SafeUint32Mult(bufferSize, pixelSize, &bufferSize) ||
		!SafeUint32Mult(bufferSize, numPlanes, &bufferSize))
		{
		ThrowMemoryFull("Arithmetic overflow computing buffer size");
		}
	
	return bufferSize;
}
Exemple #7
0
dng_basic_tag_set * dng_mask_preview::AddTagSet (dng_tiff_directory &directory) const
	{
	
	fIFD.fNewSubFileType = sfPreviewMask;
	
	fIFD.fImageWidth  = fImage->Width  ();
	fIFD.fImageLength = fImage->Height ();
	
	fIFD.fSamplesPerPixel = 1;
	
	fIFD.fPhotometricInterpretation = piTransparencyMask;
	
	fIFD.fCompression = ccDeflate;
	fIFD.fPredictor   = cpHorizontalDifference;
	
	fIFD.fCompressionQuality = fCompressionQuality;
	
	fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
	
	fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel);
	
	return new dng_basic_tag_set (directory, fIFD);
	
	}
uint32 dng_image::PixelSize () const
	{
	
	return TagTypeSize (PixelType ());
	
	}
Exemple #9
0
bool dng_info::ParseMakerNoteIFD (dng_host &host,
								  dng_stream &stream,
								  uint64 ifdSize,
						 		  uint64 ifdOffset,
								  int64 offsetDelta,
								  uint64 minOffset,
								  uint64 maxOffset,
						 		  uint32 parentCode)
	{
	
	uint32 tagIndex;
	uint32 tagCode;
	uint32 tagType;
	uint32 tagCount;
	
	// Assume there is no next IFD pointer.
	
	fMakerNoteNextIFD = 0;
	
	// If size is too small to hold a single entry IFD, abort.
	
	if (ifdSize < 14)
		{
		return false;
		}
		
	// Get entry count.
	
	stream.SetReadPosition (ifdOffset);
	
	uint32 ifdEntries = stream.Get_uint16 ();
	
	// Make the entry count if reasonable for the MakerNote size.
	
	if (ifdEntries < 1 || 2 + ifdEntries * 12 > ifdSize)
		{
		return false;
		}
		
	// Scan IFD to verify all the tag types are all valid.
		
	for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++)
		{
		
		stream.SetReadPosition (ifdOffset + 2 + tagIndex * 12 + 2);
		
		tagType = stream.Get_uint16 ();
		
		// Kludge: Some Canon MakerNotes contain tagType = 0 tags, so we
		// need to ignore them.  This was a "firmware 1.0.4" Canon 40D raw file.
		
		if (parentCode == tcCanonMakerNote && tagType == 0)
			{
			continue;
			}
		
		if (TagTypeSize (tagType) == 0)
			{
			return false;
			}
		
		}
		
	// OK, the IFD looks reasonable enough to parse.
	
	#if qDNGValidate
	
	if (gVerbose)
		{
		
		printf ("%s: Offset = %u, Entries = %u\n\n",
				LookupParentCode (parentCode),
			    (unsigned) ifdOffset, 
			    (unsigned) ifdEntries);
		
		}
		
	#endif
		
	for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++)
		{
		
		stream.SetReadPosition (ifdOffset + 2 + tagIndex * 12);
		
		tagCode  = stream.Get_uint16 ();
		tagType  = stream.Get_uint16 ();
		tagCount = stream.Get_uint32 ();
		
		if (tagType == 0)
			{
			continue;
			}
		
		uint32 tagSize = tagCount * TagTypeSize (tagType);
		
		uint64 tagOffset = ifdOffset + 2 + tagIndex * 12 + 8;
		
		if (tagSize > 4)
			{
			
			tagOffset = stream.Get_uint32 () + offsetDelta;
			
			if (tagOffset           < minOffset ||
				tagOffset + tagSize > maxOffset)
				{
				
				// Tag data is outside the valid offset range,
				// so ignore this tag.
				
				continue;
				
				}
			
			stream.SetReadPosition (tagOffset);
			
			}
			
		// Olympus switched to using IFDs in version 3 makernotes.
		
		if (parentCode == tcOlympusMakerNote &&
			tagType == ttIFD &&
			tagCount == 1)
			{
			
			uint32 olympusMakerParent = 0;
			
			switch (tagCode)
				{
				
				case 8208:
					olympusMakerParent = tcOlympusMakerNote8208;
					break;
					
				case 8224:
					olympusMakerParent = tcOlympusMakerNote8224;
					break; 
			
				case 8240:
					olympusMakerParent = tcOlympusMakerNote8240;
					break; 
			
				case 8256:
					olympusMakerParent = tcOlympusMakerNote8256;
					break; 
			
				case 8272:
					olympusMakerParent = tcOlympusMakerNote8272;
					break; 
			
				case 12288:
					olympusMakerParent = tcOlympusMakerNote12288;
					break;
					
				default:
					break;
					
				}
				
			if (olympusMakerParent)
				{
				
				stream.SetReadPosition (tagOffset);
			
				uint64 subMakerNoteOffset = stream.Get_uint32 () + offsetDelta;
				
				if (subMakerNoteOffset >= minOffset &&
					subMakerNoteOffset <  maxOffset)
					{
				
					if (ParseMakerNoteIFD (host,
										   stream,
										   maxOffset - subMakerNoteOffset,
										   subMakerNoteOffset,
										   offsetDelta,
										   minOffset,
										   maxOffset,
										   olympusMakerParent))
						{
						
						continue;
						
						}
						
					}
				
				}
				
			stream.SetReadPosition (tagOffset);
			
			}
		
		ParseTag (host,
				  stream,
				  fExif.Get (),
				  fShared.Get (),
				  NULL,
				  parentCode,
				  tagCode,
				  tagType,
				  tagCount,
				  tagOffset,
				  offsetDelta);
			
		}
		
	// Grab next IFD pointer, for possible use.
	
	if (ifdSize >= 2 + ifdEntries * 12 + 4)
		{
		
		stream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12);
		
		fMakerNoteNextIFD = stream.Get_uint32 ();
		
		}
		
	#if qDNGValidate
		
	if (gVerbose)
		{
		printf ("\n");
		}
		
	#endif
		
	return true;
		
	}
Exemple #10
0
void dng_info::ParseIFD (dng_host &host,
						 dng_stream &stream,
						 dng_exif *exif,
						 dng_shared *shared,
						 dng_ifd *ifd,
						 uint64 ifdOffset,
						 int64 offsetDelta,
						 uint32 parentCode)
	{
	
	#if qDNGValidate

	bool isMakerNote = (parentCode >= tcFirstMakerNoteIFD &&
						parentCode <= tcLastMakerNoteIFD);
	
	#endif

	stream.SetReadPosition (ifdOffset);
	
	if (ifd)
		{
		ifd->fThisIFD = ifdOffset;
		}
	
	uint32 ifdEntries = stream.Get_uint16 ();
	
	#if qDNGValidate
	
	if (gVerbose)
		{
		
		printf ("%s: Offset = %u, Entries = %u\n\n",
				LookupParentCode (parentCode),
			    (unsigned) ifdOffset, 
			    (unsigned) ifdEntries);
		
		}
		
	if ((ifdOffset & 1) && !isMakerNote)
		{
		
		char message [256];
	
		sprintf (message,
				 "%s has odd offset (%u)",
				 LookupParentCode (parentCode),
				 (unsigned) ifdOffset);
					 
		ReportWarning (message);
		
		}
		
	#endif
		
	uint32 prev_tag_code = 0;
		
	for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++)
		{
		
		stream.SetReadPosition (ifdOffset + 2 + tag_index * 12);
		
		uint32 tagCode  = stream.Get_uint16 ();
		uint32 tagType  = stream.Get_uint16 ();
		
		// Minolta 7D files have a bug in the EXIF block where the count
		// is wrong, and we run off into next IFD link.  So if abort parsing
		// if we get a zero code/type combinations.
		
		if (tagCode == 0 && tagType == 0)
			{
			
			#if qDNGValidate
			
			char message [256];
	
			sprintf (message,
					 "%s had zero/zero tag code/type entry",
					 LookupParentCode (parentCode));
					 
			ReportWarning (message);
			
			#endif
			
			return;
			
			}
		
		uint32 tagCount = stream.Get_uint32 ();
		
		#if qDNGValidate

			{
		
			if (tag_index > 0 && tagCode <= prev_tag_code && !isMakerNote)
				{
				
				char message [256];
		
				sprintf (message,
						 "%s tags are not sorted in ascending numerical order",
						 LookupParentCode (parentCode));
						 
				ReportWarning (message);
				
				}
				
			}
			
		#endif
			
		prev_tag_code = tagCode;
		
		uint32 tag_type_size = TagTypeSize (tagType);
		
		if (tag_type_size == 0)
			{
			
			#if qDNGValidate
			
				{
			
				char message [256];
		
				sprintf (message,
						 "%s %s has unknown type (%u)",
						 LookupParentCode (parentCode),
						 LookupTagCode (parentCode, tagCode),
						 (unsigned) tagType);
						 
				ReportWarning (message);
							 
				}
				
			#endif
					 
			continue;
			
			}
			
		uint64 tagOffset = ifdOffset + 2 + tag_index * 12 + 8;
		
		if (tagCount * tag_type_size > 4)
			{
			
			tagOffset = stream.Get_uint32 ();
			
			#if qDNGValidate
			
				{
			
				if (!(ifdOffset & 1) && 
				     (tagOffset & 1) &&
				    !isMakerNote     &&
				    parentCode != tcKodakDCRPrivateIFD &&
					parentCode != tcKodakKDCPrivateIFD)
					{
					
					char message [256];
		
					sprintf (message,
							 "%s %s has odd data offset (%u)",
						 	 LookupParentCode (parentCode),
						 	 LookupTagCode (parentCode, tagCode),
							 (unsigned) tagOffset);
							 
					ReportWarning (message);
						 
					}
					
				}
				
			#endif
				
			tagOffset += offsetDelta;
				
			stream.SetReadPosition (tagOffset);
			
			}
			
		ParseTag (host,
				  stream,
			      exif,
				  shared,
				  ifd,
				  parentCode,
				  tagCode,
				  tagType,
				  tagCount,
				  tagOffset,
				  offsetDelta);
			
		}
		
	stream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12);
	
	uint32 nextIFD = stream.Get_uint32 ();
	
	#if qDNGValidate
		
	if (gVerbose)
		{
		printf ("NextIFD = %u\n", (unsigned) nextIFD);
		}
		
	#endif
		
	if (ifd)
		{
		ifd->fNextIFD = nextIFD;
		}
		
	#if qDNGValidate

	if (nextIFD)
		{
		
		if (parentCode != 0 &&
				(parentCode < tcFirstChainedIFD ||
				 parentCode > tcLastChainedIFD  ))
			{

			char message [256];

			sprintf (message,
					 "%s has an unexpected non-zero NextIFD (%u)",
				 	 LookupParentCode (parentCode),
				 	 (unsigned) nextIFD);
					 
			ReportWarning (message);
					 
			}

		}
		
	if (gVerbose)
		{
		printf ("\n");
		}

	#endif
		
	}
Exemple #11
0
bool dng_info::ValidateIFD (dng_stream &stream,
						    uint64 ifdOffset,
						    int64 offsetDelta)
	{
	
	// Make sure we have a count.
	
	if (ifdOffset + 2 > stream.Length ())
		{
		return false;
		}
		
	// Get entry count.
		
	stream.SetReadPosition (ifdOffset);
	
	uint32 ifdEntries = stream.Get_uint16 ();
	
	if (ifdEntries < 1)
		{
		return false;
		}
		
	// Make sure we have room for all entries and next IFD link.
		
	if (ifdOffset + 2 + ifdEntries * 12 + 4 > stream.Length ())
		{
		return false;
		}
		
	// Check each entry.
	
	for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++)
		{
		
		stream.SetReadPosition (ifdOffset + 2 + tag_index * 12);
		
		stream.Skip (2);		// Ignore tag code.
		
		uint32 tagType  = stream.Get_uint16 ();
		uint32 tagCount = stream.Get_uint32 ();
		
		uint32 tag_type_size = TagTypeSize (tagType);
		
		if (tag_type_size == 0)
			{
			return false;
			}
			
		uint32 tag_data_size = tagCount * tag_type_size;
						
		if (tag_data_size > 4)
			{
			
			uint64 tagOffset = stream.Get_uint32 ();
							
			tagOffset += offsetDelta;
			
			if (tagOffset + tag_data_size > stream.Length ())
				{
				return false;
				}
			
			}
			
		}
		
	return true;
	
	}
Exemple #12
0
void dng_info::ParseDNGPrivateData (dng_host &host,
									dng_stream &stream)
	{
	
	if (fShared->fDNGPrivateDataCount < 2)
		{
		return;
		}
	
	// DNG private data should always start with a null-terminated 
	// company name, to define the format of the private data.
			
	dng_string privateName;
			
		{
			
		char buffer [64];
		
		stream.SetReadPosition (fShared->fDNGPrivateDataOffset);
	
		uint32 readLength = Min_uint32 (fShared->fDNGPrivateDataCount,
										sizeof (buffer) - 1);
		
		stream.Get (buffer, readLength);
		
		buffer [readLength] = 0;
		
		privateName.Set (buffer);
		
		}
		
	// Pentax is storing their MakerNote in the DNGPrivateData data.
	
	if (privateName.StartsWith ("PENTAX" ) ||
		privateName.StartsWith ("SAMSUNG"))
		{
		
		#if qDNGValidate
		
		if (gVerbose)
			{
			printf ("Parsing Pentax/Samsung DNGPrivateData\n\n");
			}
			
		#endif

		stream.SetReadPosition (fShared->fDNGPrivateDataOffset + 8);
		
		bool bigEndian = stream.BigEndian ();
		
		uint16 endianMark = stream.Get_uint16 ();
		
		if (endianMark == byteOrderMM)
			{
			bigEndian = true;
			}
			
		else if (endianMark == byteOrderII)
			{
			bigEndian = false;
			}
			
		TempBigEndian temp_endian (stream, bigEndian);
	
		ParseMakerNoteIFD (host,
						   stream,
						   fShared->fDNGPrivateDataCount - 10,
						   fShared->fDNGPrivateDataOffset + 10,
						   fShared->fDNGPrivateDataOffset,
						   fShared->fDNGPrivateDataOffset,
						   fShared->fDNGPrivateDataOffset + fShared->fDNGPrivateDataCount,
						   tcPentaxMakerNote);
						   
		return;
		
		}
				
	// Stop parsing if this is not an Adobe format block.
	
	if (!privateName.Matches ("Adobe"))
		{
		return;
		}
	
	TempBigEndian temp_order (stream);
	
	uint32 section_offset = 6;
	
	while (section_offset + 8 < fShared->fDNGPrivateDataCount)
		{
		
		stream.SetReadPosition (fShared->fDNGPrivateDataOffset + section_offset);
		
		uint32 section_key   = stream.Get_uint32 ();
		uint32 section_count = stream.Get_uint32 ();
		
		if (section_key == DNG_CHAR4 ('M','a','k','N') && section_count > 6)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found MakerNote inside DNGPrivateData\n\n");
				}
				
			#endif
				
			uint16 order_mark = stream.Get_uint16 ();
			uint64 old_offset = stream.Get_uint32 ();

			uint32 tempSize = section_count - 6;
			
			AutoPtr<dng_memory_block> tempBlock (host.Allocate (tempSize));
			
			uint64 positionInOriginalFile = stream.PositionInOriginalFile();
			
			stream.Get (tempBlock->Buffer (), tempSize);
			
			dng_stream tempStream (tempBlock->Buffer (),
								   tempSize,
								   positionInOriginalFile);
								   
			tempStream.SetBigEndian (order_mark == byteOrderMM);
			
			ParseMakerNote (host,
							tempStream,
							tempSize,
							0,
							0 - old_offset,
							0,
							tempSize);
	
			}
			
		else if (section_key == DNG_CHAR4 ('S','R','2',' ') && section_count > 6)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Sony private data inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			uint64 old_offset = stream.Get_uint32 ();

			uint64 new_offset = fShared->fDNGPrivateDataOffset + section_offset + 14;
			
			TempBigEndian sr2_order (stream, order_mark == byteOrderMM);
			
			ParseSonyPrivateData (host,
							  	  stream,
								  section_count - 6,
								  old_offset,
								  new_offset);
				
			}

		else if (section_key == DNG_CHAR4 ('R','A','F',' ') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Fuji RAF tags inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			
			uint32 tagCount = stream.Get_uint32 ();
			
			uint64 tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiHeader,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			tagCount = stream.Get_uint32 ();
			
			tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiRawInfo1,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			tagCount = stream.Get_uint32 ();
			
			tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian raf_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcFujiRAF,
						  tcFujiRawInfo2,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				stream.SetReadPosition (tagOffset + tagCount);
				
				}
			
			}

		else if (section_key == DNG_CHAR4 ('C','n','t','x') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Contax Raw header inside DNGPrivateData\n\n");
				}
				
			#endif
			
			uint16 order_mark = stream.Get_uint16 ();
			
			uint32 tagCount  = stream.Get_uint32 ();
			
			uint64 tagOffset = stream.Position ();
				
			if (tagCount)
				{
				
				TempBigEndian contax_order (stream, order_mark == byteOrderMM);
				
				ParseTag (host,
						  stream,
						  fExif.Get (),
						  fShared.Get (),
						  NULL,
						  tcContaxRAW,
						  tcContaxHeader,
						  ttUndefined,
						  tagCount,
						  tagOffset,
						  0);
						  
				}
			
			}
			
		else if (section_key == DNG_CHAR4 ('C','R','W',' ') && section_count > 4)
			{
			
			#if qDNGValidate
			
			if (gVerbose)
				{
				printf ("Found Canon CRW tags inside DNGPrivateData\n\n");
				}
				
			#endif
				
			uint16 order_mark = stream.Get_uint16 ();
			uint32 entries    = stream.Get_uint16 ();
			
			uint64 crwTagStart = stream.Position ();
			
			for (uint32 parsePass = 1; parsePass <= 2; parsePass++)
				{
				
				stream.SetReadPosition (crwTagStart);
			
				for (uint32 index = 0; index < entries; index++)
					{
					
					uint32 tagCode = stream.Get_uint16 ();
											 
					uint32 tagCount = stream.Get_uint32 ();
					
					uint64 tagOffset = stream.Position ();
					
					// We need to grab the model id tag first, and then all the
					// other tags.
					
					if ((parsePass == 1) == (tagCode == 0x5834))
						{
				
						TempBigEndian tag_order (stream, order_mark == byteOrderMM);
					
						ParseTag (host,
								  stream,
								  fExif.Get (),
								  fShared.Get (),
								  NULL,
								  tcCanonCRW,
								  tagCode,
								  ttUndefined,
								  tagCount,
								  tagOffset,
								  0);
								  
						}
					
					stream.SetReadPosition (tagOffset + tagCount);
					
					}
					
				}
			
			}

		else if (section_count > 4)
			{
			
			uint32 parentCode = 0;
			
			bool code32  = false;
			bool hasType = true;
			
			switch (section_key)
				{
				
				case DNG_CHAR4 ('M','R','W',' '):
					{
					parentCode = tcMinoltaMRW;
					code32     = true;
					hasType    = false;
					break;
					}
				
				case DNG_CHAR4 ('P','a','n','o'):
					{
					parentCode = tcPanasonicRAW;
					break;
					}
					
				case DNG_CHAR4 ('L','e','a','f'):
					{
					parentCode = tcLeafMOS;
					break;
					}
					
				case DNG_CHAR4 ('K','o','d','a'):
					{
					parentCode = tcKodakDCRPrivateIFD;
					break;
					}
					
				case DNG_CHAR4 ('K','D','C',' '):
					{
					parentCode = tcKodakKDCPrivateIFD;
					break;
					}
					
				default:
					break;
					
				}

			if (parentCode)
				{
			
				#if qDNGValidate
				
				if (gVerbose)
					{
					printf ("Found %s tags inside DNGPrivateData\n\n",
							LookupParentCode (parentCode));
					}
					
				#endif
				
				uint16 order_mark = stream.Get_uint16 ();
				uint32 entries    = stream.Get_uint16 ();
				
				for (uint32 index = 0; index < entries; index++)
					{
					
					uint32 tagCode = code32 ? stream.Get_uint32 ()
											: stream.Get_uint16 ();
											 
					uint32 tagType  = hasType ? stream.Get_uint16 () 
											  : ttUndefined;
					
					uint32 tagCount = stream.Get_uint32 ();
					
					uint32 tagSize = tagCount * TagTypeSize (tagType);
					
					uint64 tagOffset = stream.Position ();
					
					TempBigEndian tag_order (stream, order_mark == byteOrderMM);
				
					ParseTag (host,
							  stream,
							  fExif.Get (),
							  fShared.Get (),
							  NULL,
							  parentCode,
							  tagCode,
							  tagType,
							  tagCount,
							  tagOffset,
							  0);
					
					stream.SetReadPosition (tagOffset + tagSize);
					
					}
					
				}
			
			}
		
		section_offset += 8 + section_count;
		
		if (section_offset & 1)
			{
			section_offset++;
			}
		
		}
		
	}
void dng_filter_task::Process (uint32 threadIndex,
							   const dng_rect &area,
							   dng_abort_sniffer * /* sniffer */)
	{
	
	// Find source area for this destination area.
	
	dng_rect srcArea = SrcArea (area);
					  
	// Setup srcBuffer.
	
	dng_pixel_buffer srcBuffer;
	
	srcBuffer.fArea = srcArea;
								
	srcBuffer.fPlane  = fSrcPlane;
	srcBuffer.fPlanes = fSrcPlanes;
	
	srcBuffer.fPixelType  = fSrcPixelType;
	srcBuffer.fPixelSize  = TagTypeSize (fSrcPixelType);
	
	srcBuffer.fPlaneStep = RoundUpForPixelSize (srcArea.W (),
											    srcBuffer.fPixelSize);
	
	srcBuffer.fRowStep = srcBuffer.fPlaneStep *
						 srcBuffer.fPlanes;
	
	if (fSrcPixelType == fSrcImage.PixelType ())
		{
		
		srcBuffer.fPixelRange = fSrcImage.PixelRange ();
		
		}
		
	else switch (fSrcPixelType)
		{
		
		case ttByte:
		case ttSByte:
			{
			srcBuffer.fPixelRange = 0x0FF;
			break;
			}
		
		case ttShort:
		case ttSShort:
			{
			srcBuffer.fPixelRange = 0x0FFFF;
			break;
			}
		
		case ttLong:
		case ttSLong:
			{
			srcBuffer.fPixelRange = 0xFFFFFFFF;
			break;
			}
			
		case ttFloat:
			break;
			
		default:
			ThrowProgramError ();
		
		}
		
	srcBuffer.fData = fSrcBuffer [threadIndex]->Buffer ();
	
	// Setup dstBuffer.
	
	dng_pixel_buffer dstBuffer;
	
	dstBuffer.fArea = area;
	
	dstBuffer.fPlane  = fDstPlane;
	dstBuffer.fPlanes = fDstPlanes;
	
	dstBuffer.fPixelType  = fDstPixelType;
	dstBuffer.fPixelSize  = TagTypeSize (fDstPixelType);
	
	dstBuffer.fPlaneStep = RoundUpForPixelSize (area.W (),
												dstBuffer.fPixelSize);
	
	dstBuffer.fRowStep = dstBuffer.fPlaneStep *
						 dstBuffer.fPlanes;
	
	if (fDstPixelType == fDstImage.PixelType ())
		{
		
		dstBuffer.fPixelRange = fDstImage.PixelRange ();
		
		}
		
	else switch (fDstPixelType)
		{
		
		case ttByte:
		case ttSByte:
			{
			dstBuffer.fPixelRange = 0x0FF;
			break;
			}
		
		case ttShort:
		case ttSShort:
			{
			dstBuffer.fPixelRange = 0x0FFFF;
			break;
			}
		
		case ttLong:
		case ttSLong:
			{
			dstBuffer.fPixelRange = 0xFFFFFFFF;
			break;
			}
			
		case ttFloat:
			break;
			
		default:
			ThrowProgramError ();
		
		}
		
	dstBuffer.fData = fDstBuffer [threadIndex]->Buffer ();
	
	// Get source pixels.
	
	fSrcImage.Get (srcBuffer,
				   dng_image::edge_repeat,
				   fSrcRepeat.v,
				   fSrcRepeat.h);
				   
	// Process area.
	
	ProcessArea (threadIndex,
				 srcBuffer,
				 dstBuffer);

	// Save result pixels.
	
	fDstImage.Put (dstBuffer);
	
	}
void dng_filter_task::Process (uint32 threadIndex,
							   const dng_rect &area,
							   dng_abort_sniffer * /* sniffer */)
	{
	
	// Find source area for this destination area.
	
	dng_rect srcArea = SrcArea (area);
					  
	// Setup srcBuffer.
	
	dng_pixel_buffer srcBuffer;
	
	srcBuffer.fArea = srcArea;
								
	srcBuffer.fPlane  = fSrcPlane;
	srcBuffer.fPlanes = fSrcPlanes;
	
	srcBuffer.fPixelType  = fSrcPixelType;
	srcBuffer.fPixelSize  = TagTypeSize (fSrcPixelType);
	
	srcBuffer.fPlaneStep = RoundUpForPixelSize (srcArea.W (),
											    srcBuffer.fPixelSize);
	
	srcBuffer.fRowStep = srcBuffer.fPlaneStep *
						 srcBuffer.fPlanes;
		
	srcBuffer.fData = fSrcBuffer [threadIndex]->Buffer ();
	
	// Setup dstBuffer.
	
	dng_pixel_buffer dstBuffer;
	
	dstBuffer.fArea = area;
	
	dstBuffer.fPlane  = fDstPlane;
	dstBuffer.fPlanes = fDstPlanes;
	
	dstBuffer.fPixelType  = fDstPixelType;
	dstBuffer.fPixelSize  = TagTypeSize (fDstPixelType);
	
	dstBuffer.fPlaneStep = RoundUpForPixelSize (area.W (),
												dstBuffer.fPixelSize);
	
	dstBuffer.fRowStep = dstBuffer.fPlaneStep *
						 dstBuffer.fPlanes;
			
	dstBuffer.fData = fDstBuffer [threadIndex]->Buffer ();
	
	// Get source pixels.
	
	fSrcImage.Get (srcBuffer,
				   dng_image::edge_repeat,
				   fSrcRepeat.v,
				   fSrcRepeat.h);
				   
	// Process area.
	
	ProcessArea (threadIndex,
				 srcBuffer,
				 dstBuffer);

	// Save result pixels.
	
	fDstImage.Put (dstBuffer);
	
	}
Exemple #15
0
bool dng_camera_profile_info::ParseExtended (dng_stream &stream)
	{

	try
		{

		// Offsets are relative to the start of this structure, not the entire file.

		uint64 startPosition = stream.Position ();

		// Read header. Like a TIFF header, but with different magic number
		// Plus all offsets are relative to the start of the IFD, not to the
		// stream or file.

		uint16 byteOrder = stream.Get_uint16 ();

		if (byteOrder == byteOrderMM)
			fBigEndian = true;

		else if (byteOrder == byteOrderII)
			fBigEndian = false;

		else
			return false;

		TempBigEndian setEndianness (stream, fBigEndian);

		uint16 magicNumber = stream.Get_uint16 ();

		if (magicNumber != magicExtendedProfile)
			{
			return false;
			}

		uint32 offset = stream.Get_uint32 ();

		stream.Skip (offset - 8);

		// Start on IFD entries.

		uint32 ifdEntries = stream.Get_uint16 ();

		if (ifdEntries < 1)
			{
			return false;
			}

		for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++)
			{

			stream.SetReadPosition (startPosition + 8 + 2 + tag_index * 12);

			uint16 tagCode  = stream.Get_uint16 ();
			uint32 tagType  = stream.Get_uint16 ();
			uint32 tagCount = stream.Get_uint32 ();

			uint64 tagOffset = stream.Position ();

			if (TagTypeSize (tagType) * tagCount > 4)
				{

				tagOffset = startPosition + stream.Get_uint32 ();

				stream.SetReadPosition (tagOffset);

				}

			if (!ParseTag (stream,
						   0,
						   tagCode,
						   tagType,
						   tagCount,
						   tagOffset))
				{

				#if qDNGValidate

				if (gVerbose)
					{

					stream.SetReadPosition (tagOffset);

					printf ("*");

					DumpTagValues (stream,
								   LookupTagType (tagType),
								   0,
								   tagCode,
								   tagType,
								   tagCount);

					}

				#endif

				}

			}

		return true;

		}

	catch (...)
		{

		// Eat parsing errors.

		}

	return false;

	}
Exemple #16
0
bool dng_shared::Parse_ifd0 (dng_stream &stream,
							 dng_exif & /* exif */,
							 uint32 parentCode,
							 uint32 tagCode,
							 uint32 tagType,
							 uint32 tagCount,
							 uint64 tagOffset)
	{

	switch (tagCode)
		{

		case tcXMP:
			{

			CheckTagType (parentCode, tagCode, tagType, ttByte);

			fXMPCount  = tagCount;
			fXMPOffset = fXMPCount ? tagOffset : 0;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("XMP: Count = %u, Offset = %u\n",
						(unsigned) fXMPCount,
						(unsigned) fXMPOffset);

				if (fXMPCount)
					{

					DumpXMP (stream, fXMPCount);

					}

				}

			#endif

			break;

			}

		case tcIPTC_NAA:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong, ttAscii);

			fIPTC_NAA_Count  = tagCount * TagTypeSize (tagType);
			fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("IPTC/NAA: Count = %u, Offset = %u\n",
						(unsigned) fIPTC_NAA_Count,
						(unsigned) fIPTC_NAA_Offset);

				if (fIPTC_NAA_Count)
					{

					DumpHexAscii (stream, fIPTC_NAA_Count);

					}

				// Compute and output the digest.

				dng_memory_data buffer (fIPTC_NAA_Count);

				stream.SetReadPosition (fIPTC_NAA_Offset);

				stream.Get (buffer.Buffer (), fIPTC_NAA_Count);

				const uint8 *data = buffer.Buffer_uint8 ();

				uint32 count = fIPTC_NAA_Count;

				// Method 1: Counting all bytes (this is correct).

					{

					dng_md5_printer printer;

					printer.Process (data, count);

					printf ("IPTCDigest: ");

					DumpFingerprint (printer.Result ());

					printf ("\n");

					}

				// Method 2: Ignoring zero padding.

					{

					uint32 removed = 0;

					while ((removed < 3) && (count > 0) && (data [count - 1] == 0))
						{
						removed++;
						count--;
						}

					if (removed != 0)
						{

						dng_md5_printer printer;

						printer.Process (data, count);

						printf ("IPTCDigest (ignoring zero padding): ");

						DumpFingerprint (printer.Result ());

						printf ("\n");

						}

					}

				}

			#endif

			break;

			}

		case tcExifIFD:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fExifIFD = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("ExifIFD: %u\n", (unsigned) fExifIFD);
				}

			#endif

			break;

			}

		case tcGPSInfo:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fGPSInfo = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("GPSInfo: %u\n", (unsigned) fGPSInfo);
				}

			#endif

			break;

			}

		case tcKodakDCRPrivateIFD:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fKodakDCRPrivateIFD = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("KodakDCRPrivateIFD: %u\n", (unsigned) fKodakDCRPrivateIFD);
				}

			#endif

			break;

			}

		case tcKodakKDCPrivateIFD:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fKodakKDCPrivateIFD = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("KodakKDCPrivateIFD: %u\n", (unsigned) fKodakKDCPrivateIFD);
				}

			#endif

			break;

			}

		case tcDNGVersion:
			{

			CheckTagType (parentCode, tagCode, tagType, ttByte);

			CheckTagCount (parentCode, tagCode, tagCount, 4);

			uint32 b0 = stream.Get_uint8 ();
			uint32 b1 = stream.Get_uint8 ();
			uint32 b2 = stream.Get_uint8 ();
			uint32 b3 = stream.Get_uint8 ();

			fDNGVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("DNGVersion: %u.%u.%u.%u\n",
						(unsigned) b0,
						(unsigned) b1,
						(unsigned) b2,
						(unsigned) b3);
				}

			#endif

			break;

			}

		case tcDNGBackwardVersion:
			{

			CheckTagType (parentCode, tagCode, tagType, ttByte);

			CheckTagCount (parentCode, tagCode, tagCount, 4);

			uint32 b0 = stream.Get_uint8 ();
			uint32 b1 = stream.Get_uint8 ();
			uint32 b2 = stream.Get_uint8 ();
			uint32 b3 = stream.Get_uint8 ();

			fDNGBackwardVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;

			#if qDNGValidate

			if (gVerbose)
				{
				printf ("DNGBackwardVersion: %u.%u.%u.%u\n",
						(unsigned) b0,
						(unsigned) b1,
						(unsigned) b2,
						(unsigned) b3);
				}

			#endif

			break;

			}

		case tcUniqueCameraModel:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fUniqueCameraModel,
							false);

			bool didTrim = fUniqueCameraModel.TrimTrailingBlanks ();

			#if qDNGValidate

			if (didTrim)
				{

				ReportWarning ("UniqueCameraModel string has trailing blanks");

				}

			if (gVerbose)
				{

				printf ("UniqueCameraModel: ");

				DumpString (fUniqueCameraModel);

				printf ("\n");

				}

			#else

			(void) didTrim;		// Unused

			#endif

			break;

			}

		case tcLocalizedCameraModel:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fLocalizedCameraModel,
							false,
							false);

			bool didTrim = fLocalizedCameraModel.TrimTrailingBlanks ();

			#if qDNGValidate

			if (didTrim)
				{

				ReportWarning ("LocalizedCameraModel string has trailing blanks");

				}

			if (gVerbose)
				{

				printf ("LocalizedCameraModel: ");

				DumpString (fLocalizedCameraModel);

				printf ("\n");

				}

			#else

			(void) didTrim;		// Unused

			#endif

			break;

			}

		case tcCameraCalibration1:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fCameraProfile.fColorPlanes,
								 fCameraProfile.fColorPlanes,
								 fCameraCalibration1))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CameraCalibration1:\n");

				DumpMatrix (fCameraCalibration1);

				}

			#endif

			break;

			}

		case tcCameraCalibration2:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fCameraProfile.fColorPlanes,
								 fCameraProfile.fColorPlanes,
								 fCameraCalibration2))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CameraCalibration2:\n");

				DumpMatrix (fCameraCalibration2);

				}

			#endif

			break;

			}

		case tcCameraCalibrationSignature:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fCameraCalibrationSignature,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CameraCalibrationSignature: ");

				DumpString (fCameraCalibrationSignature);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcAnalogBalance:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			if (!ParseVectorTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fCameraProfile.fColorPlanes,
								 fAnalogBalance))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AnalogBalance:");

				DumpVector (fAnalogBalance);

				}

			#endif

			break;

			}

		case tcAsShotNeutral:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			if (!ParseVectorTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 fCameraProfile.fColorPlanes,
								 fAsShotNeutral))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AsShotNeutral:");

				DumpVector (fAsShotNeutral);

				}

			#endif

			break;

			}

		case tcAsShotWhiteXY:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
				return false;

			fAsShotWhiteXY.x = stream.TagValue_real64 (tagType);
			fAsShotWhiteXY.y = stream.TagValue_real64 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AsShotWhiteXY: %0.4f %0.4f\n",
						fAsShotWhiteXY.x,
						fAsShotWhiteXY.y);

				}

			#endif

			break;

			}

		case tcBaselineExposure:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fBaselineExposure = stream.TagValue_srational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("BaselineExposure: %+0.2f\n",
					    fBaselineExposure.As_real64 ());

				}

			#endif

			break;

			}

		case tcBaselineNoise:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fBaselineNoise = stream.TagValue_urational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("BaselineNoise: %0.2f\n",
						fBaselineNoise.As_real64 ());

				}

			#endif

			break;

			}

		case tcNoiseReductionApplied:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 1))
				return false;

			fNoiseReductionApplied = stream.TagValue_urational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("NoiseReductionApplied: %u/%u\n",
						(unsigned) fNoiseReductionApplied.n,
						(unsigned) fNoiseReductionApplied.d);

				}

			#endif

			break;

			}

		case tcNoiseProfile:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttDouble))
				return false;

			// Must be an even, positive number of doubles in a noise profile.

			if (!tagCount || (tagCount & 1))
				return false;

			// Determine number of planes (i.e., half the number of doubles).

			const uint32 numPlanes = Pin_uint32 (0,
												 tagCount >> 1,
												 kMaxColorPlanes);

			// Parse the noise function parameters.

			std::vector<dng_noise_function> noiseFunctions;

			for (uint32 i = 0; i < numPlanes; i++)
				{

				const real64 scale	= stream.TagValue_real64 (tagType);
				const real64 offset = stream.TagValue_real64 (tagType);

				noiseFunctions.push_back (dng_noise_function (scale, offset));

				}

			// Store the noise profile.

			fNoiseProfile = dng_noise_profile (noiseFunctions);

			// Debug.

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("NoiseProfile:\n");

				printf ("  Planes: %u\n", numPlanes);

				for (uint32 plane = 0; plane < numPlanes; plane++)
					{

					printf ("  Noise function for plane %u: scale = %.8lf, offset = %.8lf\n",
							plane,
							noiseFunctions [plane].Scale  (),
							noiseFunctions [plane].Offset ());

					}

				}

			#endif

			break;

			}

		case tcBaselineSharpness:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fBaselineSharpness = stream.TagValue_urational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("BaselineSharpness: %0.2f\n",
					    fBaselineSharpness.As_real64 ());

				}

			#endif

			break;

			}

		case tcLinearResponseLimit:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fLinearResponseLimit = stream.TagValue_urational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("LinearResponseLimit: %0.2f\n",
						fLinearResponseLimit.As_real64 ());

				}

			#endif

			break;

			}

		case tcShadowScale:
			{

			CheckTagType (parentCode, tagCode, tagType, ttRational);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fShadowScale = stream.TagValue_urational (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ShadowScale: %0.4f\n",
						fShadowScale.As_real64 ());

				}

			#endif

			break;

			}

		case tcDNGPrivateData:
			{

			CheckTagType (parentCode, tagCode, tagType, ttByte);

			fDNGPrivateDataCount  = tagCount;
			fDNGPrivateDataOffset = tagOffset;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("DNGPrivateData: Count = %u, Offset = %u\n",
						(unsigned) fDNGPrivateDataCount,
						(unsigned) fDNGPrivateDataOffset);

				DumpHexAscii (stream, tagCount);

				}

			#endif

			break;

			}

		case tcMakerNoteSafety:
			{

			CheckTagType (parentCode, tagCode, tagType, ttShort);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fMakerNoteSafety = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("MakerNoteSafety: %s\n",
						LookupMakerNoteSafety (fMakerNoteSafety));

				}

			#endif

			break;

			}

		case tcRawImageDigest:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
				return false;

			stream.Get (fRawImageDigest.data, 16);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("RawImageDigest: ");

				DumpFingerprint (fRawImageDigest);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcRawDataUniqueID:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
				return false;

			stream.Get (fRawDataUniqueID.data, 16);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("RawDataUniqueID: ");

				DumpFingerprint (fRawDataUniqueID);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcOriginalRawFileName:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fOriginalRawFileName,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("OriginalRawFileName: ");

				DumpString (fOriginalRawFileName);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcOriginalRawFileData:
			{

			CheckTagType (parentCode, tagCode, tagType, ttUndefined);

			fOriginalRawFileDataCount  = tagCount;
			fOriginalRawFileDataOffset = tagOffset;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("OriginalRawFileData: Count = %u, Offset = %u\n",
						(unsigned) fOriginalRawFileDataCount,
						(unsigned) fOriginalRawFileDataOffset);

				DumpHexAscii (stream, tagCount);

				}

			#endif

			break;

			}

		case tcOriginalRawFileDigest:
			{

			if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
				return false;

			if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
				return false;

			stream.Get (fOriginalRawFileDigest.data, 16);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("OriginalRawFileDigest: ");

				DumpFingerprint (fOriginalRawFileDigest);

				printf ("\n");

				}

			#endif

			break;

			}

		case tcAsShotICCProfile:
			{

			CheckTagType (parentCode, tagCode, tagType, ttUndefined);

			fAsShotICCProfileCount  = tagCount;
			fAsShotICCProfileOffset = tagOffset;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AsShotICCProfile: Count = %u, Offset = %u\n",
						(unsigned) fAsShotICCProfileCount,
						(unsigned) fAsShotICCProfileOffset);

				DumpHexAscii (stream, tagCount);

				}

			#endif

			break;

			}

		case tcAsShotPreProfileMatrix:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			uint32 rows = fCameraProfile.fColorPlanes;

			if (tagCount == fCameraProfile.fColorPlanes * 3)
				{
				rows = 3;
				}

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 rows,
								 fCameraProfile.fColorPlanes,
								 fAsShotPreProfileMatrix))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AsShotPreProfileMatrix:\n");

				DumpMatrix (fAsShotPreProfileMatrix);

				}

			#endif

			break;

			}

		case tcCurrentICCProfile:
			{

			CheckTagType (parentCode, tagCode, tagType, ttUndefined);

			fCurrentICCProfileCount  = tagCount;
			fCurrentICCProfileOffset = tagOffset;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CurrentICCProfile: Count = %u, Offset = %u\n",
						(unsigned) fCurrentICCProfileCount,
						(unsigned) fCurrentICCProfileOffset);

				DumpHexAscii (stream, tagCount);

				}

			#endif

			break;

			}

		case tcCurrentPreProfileMatrix:
			{

			CheckTagType (parentCode, tagCode, tagType, ttSRational);

			if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes))
				return false;

			uint32 rows = fCameraProfile.fColorPlanes;

			if (tagCount == fCameraProfile.fColorPlanes * 3)
				{
				rows = 3;
				}

			if (!ParseMatrixTag (stream,
								 parentCode,
								 tagCode,
								 tagType,
								 tagCount,
								 rows,
								 fCameraProfile.fColorPlanes,
								 fCurrentPreProfileMatrix))
				return false;

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("CurrentPreProfileMatrix:\n");

				DumpMatrix (fCurrentPreProfileMatrix);

				}

			#endif

			break;

			}

		case tcColorimetricReference:
			{

			CheckTagType (parentCode, tagCode, tagType, ttShort);

			CheckTagCount (parentCode, tagCode, tagCount, 1);

			fColorimetricReference = stream.TagValue_uint32 (tagType);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ColorimetricReference: %s\n",
						LookupColorimetricReference (fColorimetricReference));

				}

			#endif

			break;

			}

		case tcExtraCameraProfiles:
			{

			CheckTagType (parentCode, tagCode, tagType, ttLong);

			CheckTagCount (parentCode, tagCode, tagCount, 1, tagCount);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("ExtraCameraProfiles: %u\n", (unsigned) tagCount);

				}

			#endif

			fExtraCameraProfiles.reserve (tagCount);

			for (uint32 index = 0; index < tagCount; index++)
				{

				#if qDNGValidate

				if (gVerbose)
					{

					printf ("\nExtraCameraProfile [%u]:\n\n", (unsigned) index);

					}

				#endif

				stream.SetReadPosition (tagOffset + index * 4);

				uint32 profileOffset = stream.TagValue_uint32 (tagType);

				dng_camera_profile_info profileInfo;

				stream.SetReadPosition (profileOffset);

				if (profileInfo.ParseExtended (stream))
					{

					fExtraCameraProfiles.push_back (profileInfo);

					}

				else
					{

					#if qDNGValidate

					ReportWarning ("Unable to parse extra camera profile");

					#endif

					}

				}

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("\nDone with ExtraCameraProfiles\n\n");

				}

			#endif

			break;

			}

		case tcAsShotProfileName:
			{

			CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);

			ParseStringTag (stream,
							parentCode,
							tagCode,
							tagCount,
							fAsShotProfileName,
							false,
							false);

			#if qDNGValidate

			if (gVerbose)
				{

				printf ("AsShotProfileName: ");

				DumpString (fAsShotProfileName);

				printf ("\n");

				}

			#endif

			break;

			}

		default:
			{

			// The main camera profile tags also appear in IFD 0

			return fCameraProfile.ParseTag (stream,
											parentCode,
											tagCode,
											tagType,
											tagCount,
											tagOffset);

			}

		}

	return true;

	}