blob: 5c810426e6e621f8b0ff91a8a0d4574aed4fa786 [file] [log] [blame]
/*****************************************************************************/
// Copyright 2006-2012 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image_writer.cpp#4 $ */
/* $DateTime: 2012/06/14 20:24:41 $ */
/* $Change: 835078 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_image_writer.h"
#include "dng_abort_sniffer.h"
#include "dng_area_task.h"
#include "dng_bottlenecks.h"
#include "dng_camera_profile.h"
#include "dng_color_space.h"
#include "dng_exif.h"
#include "dng_flags.h"
#include "dng_exceptions.h"
#include "dng_host.h"
#include "dng_ifd.h"
#include "dng_image.h"
#include "dng_jpeg_image.h"
#include "dng_lossless_jpeg.h"
#include "dng_memory.h"
#include "dng_memory_stream.h"
#include "dng_negative.h"
#include "dng_pixel_buffer.h"
#include "dng_preview.h"
#include "dng_read_image.h"
#include "dng_safe_arithmetic.h"
#include "dng_stream.h"
#include "dng_string_list.h"
#include "dng_tag_codes.h"
#include "dng_tag_values.h"
#include "dng_utils.h"
#if qDNGUseXMP
#include "dng_xmp.h"
#endif
#include "zlib.h"
#if qDNGUseLibJPEG
#include "dng_jpeglib.h"
#endif
/*****************************************************************************/
// Defines for testing DNG 1.2 features.
//#define qTestRowInterleave 2
//#define qTestSubTileBlockRows 2
//#define qTestSubTileBlockCols 2
/*****************************************************************************/
dng_resolution::dng_resolution ()
: fXResolution ()
, fYResolution ()
, fResolutionUnit (0)
{
}
/******************************************************************************/
static void SpoolAdobeData (dng_stream &stream,
const dng_metadata *metadata,
const dng_jpeg_preview *preview,
const dng_memory_block *imageResources)
{
TempBigEndian tempEndian (stream);
#if qDNGUseXMP
if (metadata && metadata->GetXMP ())
{
bool marked = false;
if (metadata->GetXMP ()->GetBoolean (XMP_NS_XAP_RIGHTS,
"Marked",
marked))
{
stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
stream.Put_uint16 (1034);
stream.Put_uint16 (0);
stream.Put_uint32 (1);
stream.Put_uint8 (marked ? 1 : 0);
stream.Put_uint8 (0);
}
dng_string webStatement;
if (metadata->GetXMP ()->GetString (XMP_NS_XAP_RIGHTS,
"WebStatement",
webStatement))
{
dng_memory_data buffer;
uint32 size = webStatement.Get_SystemEncoding (buffer);
if (size > 0)
{
stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
stream.Put_uint16 (1035);
stream.Put_uint16 (0);
stream.Put_uint32 (size);
stream.Put (buffer.Buffer (), size);
if (size & 1)
stream.Put_uint8 (0);
}
}
}
#endif
if (preview)
{
preview->SpoolAdobeThumbnail (stream);
}
if (metadata && metadata->IPTCLength ())
{
dng_fingerprint iptcDigest = metadata->IPTCDigest ();
if (iptcDigest.IsValid ())
{
stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
stream.Put_uint16 (1061);
stream.Put_uint16 (0);
stream.Put_uint32 (16);
stream.Put (iptcDigest.data, 16);
}
}
if (imageResources)
{
uint32 size = imageResources->LogicalSize ();
stream.Put (imageResources->Buffer (), size);
if (size & 1)
stream.Put_uint8 (0);
}
}
/******************************************************************************/
static dng_memory_block * BuildAdobeData (dng_host &host,
const dng_metadata *metadata,
const dng_jpeg_preview *preview,
const dng_memory_block *imageResources)
{
dng_memory_stream stream (host.Allocator ());
SpoolAdobeData (stream,
metadata,
preview,
imageResources);
return stream.AsMemoryBlock (host.Allocator ());
}
/*****************************************************************************/
tag_string::tag_string (uint16 code,
const dng_string &s,
bool forceASCII)
: tiff_tag (code, ttAscii, 0)
, fString (s)
{
if (forceASCII)
{
// Metadata working group recommendation - go ahead
// write UTF-8 into ASCII tag strings, rather than
// actually force the strings to ASCII. There is a matching
// change on the reading side to assume UTF-8 if the string
// contains a valid UTF-8 string.
//
// fString.ForceASCII ();
}
else if (!fString.IsASCII ())
{
fType = ttByte;
}
fCount = fString.Length () + 1;
}
/*****************************************************************************/
void tag_string::Put (dng_stream &stream) const
{
stream.Put (fString.Get (), Size ());
}
/*****************************************************************************/
tag_encoded_text::tag_encoded_text (uint16 code,
const dng_string &text)
: tiff_tag (code, ttUndefined, 0)
, fText (text)
, fUTF16 ()
{
if (fText.IsASCII ())
{
fCount = 8 + fText.Length ();
}
else
{
fCount = 8 + fText.Get_UTF16 (fUTF16) * 2;
}
}
/*****************************************************************************/
void tag_encoded_text::Put (dng_stream &stream) const
{
if (fUTF16.Buffer ())
{
stream.Put ("UNICODE\000", 8);
uint32 chars = (fCount - 8) >> 1;
const uint16 *buf = fUTF16.Buffer_uint16 ();
for (uint32 j = 0; j < chars; j++)
{
stream.Put_uint16 (buf [j]);
}
}
else
{
stream.Put ("ASCII\000\000\000", 8);
stream.Put (fText.Get (), fCount - 8);
}
}
/*****************************************************************************/
void tag_data_ptr::Put (dng_stream &stream) const
{
// If we are swapping bytes, we need to swap with the right size
// entries.
if (stream.SwapBytes ())
{
switch (Type ())
{
// Two byte entries.
case ttShort:
case ttSShort:
case ttUnicode:
{
const uint16 *p = (const uint16 *) fData;
uint32 entries = (Size () >> 1);
for (uint32 j = 0; j < entries; j++)
{
stream.Put_uint16 (p [j]);
}
return;
}
// Four byte entries.
case ttLong:
case ttSLong:
case ttRational:
case ttSRational:
case ttIFD:
case ttFloat:
case ttComplex:
{
const uint32 *p = (const uint32 *) fData;
uint32 entries = (Size () >> 2);
for (uint32 j = 0; j < entries; j++)
{
stream.Put_uint32 (p [j]);
}
return;
}
// Eight byte entries.
case ttDouble:
{
const real64 *p = (const real64 *) fData;
uint32 entries = (Size () >> 3);
for (uint32 j = 0; j < entries; j++)
{
stream.Put_real64 (p [j]);
}
return;
}
// Entries don't need to be byte swapped. Fall through
// to non-byte swapped case.
default:
{
break;
}
}
}
// Non-byte swapped case.
stream.Put (fData, Size ());
}
/******************************************************************************/
tag_matrix::tag_matrix (uint16 code,
const dng_matrix &m)
: tag_srational_ptr (code, fEntry, m.Rows () * m.Cols ())
{
uint32 index = 0;
for (uint32 r = 0; r < m.Rows (); r++)
for (uint32 c = 0; c < m.Cols (); c++)
{
fEntry [index].Set_real64 (m [r] [c], 10000);
index++;
}
}
/******************************************************************************/
tag_icc_profile::tag_icc_profile (const void *profileData,
uint32 profileSize)
: tag_data_ptr (tcICCProfile,
ttUndefined,
0,
NULL)
{
if (profileData && profileSize)
{
SetCount (profileSize);
SetData (profileData);
}
}
/******************************************************************************/
void tag_cfa_pattern::Put (dng_stream &stream) const
{
stream.Put_uint16 ((uint16) fCols);
stream.Put_uint16 ((uint16) fRows);
for (uint32 col = 0; col < fCols; col++)
for (uint32 row = 0; row < fRows; row++)
{
stream.Put_uint8 (fPattern [row * kMaxCFAPattern + col]);
}
}
/******************************************************************************/
tag_exif_date_time::tag_exif_date_time (uint16 code,
const dng_date_time &dt)
: tag_data_ptr (code, ttAscii, 20, fData)
{
if (dt.IsValid ())
{
sprintf (fData,
"%04d:%02d:%02d %02d:%02d:%02d",
(int) dt.fYear,
(int) dt.fMonth,
(int) dt.fDay,
(int) dt.fHour,
(int) dt.fMinute,
(int) dt.fSecond);
}
}
/******************************************************************************/
tag_iptc::tag_iptc (const void *data,
uint32 length)
: tiff_tag (tcIPTC_NAA, ttLong, (length + 3) >> 2)
, fData (data )
, fLength (length)
{
}
/******************************************************************************/
void tag_iptc::Put (dng_stream &stream) const
{
// Note: For historical compatiblity reasons, the standard TIFF data
// type for IPTC data is ttLong, but without byte swapping. This really
// should be ttUndefined, but doing the right thing would break some
// existing readers.
stream.Put (fData, fLength);
// Pad with zeros to get to long word boundary.
uint32 extra = fCount * 4 - fLength;
while (extra--)
{
stream.Put_uint8 (0);
}
}
/******************************************************************************/
tag_xmp::tag_xmp (const dng_xmp *xmp)
: tag_uint8_ptr (tcXMP, NULL, 0)
, fBuffer ()
{
#if qDNGUseXMP
if (xmp)
{
fBuffer.Reset (xmp->Serialize (true));
if (fBuffer.Get ())
{
SetData (fBuffer->Buffer_uint8 ());
SetCount (fBuffer->LogicalSize ());
}
}
#endif
}
/******************************************************************************/
void dng_tiff_directory::Add (const tiff_tag *tag)
{
if (fEntries >= kMaxEntries)
{
ThrowProgramError ();
}
// Tags must be sorted in increasing order of tag code.
uint32 index = fEntries;
for (uint32 j = 0; j < fEntries; j++)
{
if (tag->Code () < fTag [j]->Code ())
{
index = j;
break;
}
}
for (uint32 k = fEntries; k > index; k--)
{
fTag [k] = fTag [k - 1];
}
fTag [index] = tag;
fEntries++;
}
/******************************************************************************/
uint32 dng_tiff_directory::Size () const
{
if (!fEntries) return 0;
uint32 size = fEntries * 12 + 6;
for (uint32 index = 0; index < fEntries; index++)
{
uint32 tagSize = fTag [index]->Size ();
if (tagSize > 4)
{
size += (tagSize + 1) & ~1;
}
}
return size;
}
/******************************************************************************/
void dng_tiff_directory::Put (dng_stream &stream,
OffsetsBase offsetsBase,
uint32 explicitBase) const
{
if (!fEntries) return;
uint32 index;
uint32 bigData = fEntries * 12 + 6;
if (offsetsBase == offsetsRelativeToStream)
bigData += (uint32) stream.Position ();
else if (offsetsBase == offsetsRelativeToExplicitBase)
bigData += explicitBase;
stream.Put_uint16 ((uint16) fEntries);
for (index = 0; index < fEntries; index++)
{
const tiff_tag &tag = *fTag [index];
stream.Put_uint16 (tag.Code ());
stream.Put_uint16 (tag.Type ());
stream.Put_uint32 (tag.Count ());
uint32 size = tag.Size ();
if (size <= 4)
{
tag.Put (stream);
while (size < 4)
{
stream.Put_uint8 (0);
size++;
}
}
else
{
stream.Put_uint32 (bigData);
bigData += (size + 1) & ~1;
}
}
stream.Put_uint32 (fChained); // Next IFD offset
for (index = 0; index < fEntries; index++)
{
const tiff_tag &tag = *fTag [index];
uint32 size = tag.Size ();
if (size > 4)
{
tag.Put (stream);
if (size & 1)
stream.Put_uint8 (0);
}
}
}
/******************************************************************************/
dng_basic_tag_set::dng_basic_tag_set (dng_tiff_directory &directory,
const dng_ifd &info)
: fNewSubFileType (tcNewSubFileType, info.fNewSubFileType)
, fImageWidth (tcImageWidth , info.fImageWidth )
, fImageLength (tcImageLength, info.fImageLength)
, fPhotoInterpretation (tcPhotometricInterpretation,
(uint16) info.fPhotometricInterpretation)
, fFillOrder (tcFillOrder, 1)
, fSamplesPerPixel (tcSamplesPerPixel, (uint16) info.fSamplesPerPixel)
, fBitsPerSample (tcBitsPerSample,
fBitsPerSampleData,
info.fSamplesPerPixel)
, fStrips (info.fUsesStrips)
, fTileWidth (tcTileWidth, info.fTileWidth)
, fTileLength (fStrips ? tcRowsPerStrip : tcTileLength,
info.fTileLength)
, fTileInfoBuffer (info.TilesPerImage (), 8)
, fTileOffsetData (fTileInfoBuffer.Buffer_uint32 ())
, fTileOffsets (fStrips ? tcStripOffsets : tcTileOffsets,
fTileOffsetData,
info.TilesPerImage ())
, fTileByteCountData (fTileOffsetData + info.TilesPerImage ())
, fTileByteCounts (fStrips ? tcStripByteCounts : tcTileByteCounts,
fTileByteCountData,
info.TilesPerImage ())
, fPlanarConfiguration (tcPlanarConfiguration, pcInterleaved)
, fCompression (tcCompression, (uint16) info.fCompression)
, fPredictor (tcPredictor , (uint16) info.fPredictor )
, fExtraSamples (tcExtraSamples,
fExtraSamplesData,
info.fExtraSamplesCount)
, fSampleFormat (tcSampleFormat,
fSampleFormatData,
info.fSamplesPerPixel)
, fRowInterleaveFactor (tcRowInterleaveFactor,
(uint16) info.fRowInterleaveFactor)
, fSubTileBlockSize (tcSubTileBlockSize,
fSubTileBlockSizeData,
2)
{
uint32 j;
for (j = 0; j < info.fSamplesPerPixel; j++)
{
fBitsPerSampleData [j] = (uint16) info.fBitsPerSample [0];
}
directory.Add (&fNewSubFileType);
directory.Add (&fImageWidth);
directory.Add (&fImageLength);
directory.Add (&fPhotoInterpretation);
directory.Add (&fSamplesPerPixel);
directory.Add (&fBitsPerSample);
if (info.fBitsPerSample [0] != 8 &&
info.fBitsPerSample [0] != 16 &&
info.fBitsPerSample [0] != 32)
{
directory.Add (&fFillOrder);
}
if (!fStrips)
{
directory.Add (&fTileWidth);
}
directory.Add (&fTileLength);
directory.Add (&fTileOffsets);
directory.Add (&fTileByteCounts);
directory.Add (&fPlanarConfiguration);
directory.Add (&fCompression);
if (info.fPredictor != cpNullPredictor)
{
directory.Add (&fPredictor);
}
if (info.fExtraSamplesCount != 0)
{
for (j = 0; j < info.fExtraSamplesCount; j++)
{
fExtraSamplesData [j] = (uint16) info.fExtraSamples [j];
}
directory.Add (&fExtraSamples);
}
if (info.fSampleFormat [0] != sfUnsignedInteger)
{
for (j = 0; j < info.fSamplesPerPixel; j++)
{
fSampleFormatData [j] = (uint16) info.fSampleFormat [j];
}
directory.Add (&fSampleFormat);
}
if (info.fRowInterleaveFactor != 1)
{
directory.Add (&fRowInterleaveFactor);
}
if (info.fSubTileBlockRows != 1 ||
info.fSubTileBlockCols != 1)
{
fSubTileBlockSizeData [0] = (uint16) info.fSubTileBlockRows;
fSubTileBlockSizeData [1] = (uint16) info.fSubTileBlockCols;
directory.Add (&fSubTileBlockSize);
}
}
/******************************************************************************/
exif_tag_set::exif_tag_set (dng_tiff_directory &directory,
const dng_exif &exif,
bool makerNoteSafe,
const void *makerNoteData,
uint32 makerNoteLength,
bool insideDNG)
: fExifIFD ()
, fGPSIFD ()
, fExifLink (tcExifIFD, 0)
, fGPSLink (tcGPSInfo, 0)
, fAddedExifLink (false)
, fAddedGPSLink (false)
, fExifVersion (tcExifVersion, ttUndefined, 4, fExifVersionData)
, fExposureTime (tcExposureTime , exif.fExposureTime )
, fShutterSpeedValue (tcShutterSpeedValue, exif.fShutterSpeedValue)
, fFNumber (tcFNumber , exif.fFNumber )
, fApertureValue (tcApertureValue, exif.fApertureValue)
, fBrightnessValue (tcBrightnessValue, exif.fBrightnessValue)
, fExposureBiasValue (tcExposureBiasValue, exif.fExposureBiasValue)
, fMaxApertureValue (tcMaxApertureValue , exif.fMaxApertureValue)
, fSubjectDistance (tcSubjectDistance, exif.fSubjectDistance)
, fFocalLength (tcFocalLength, exif.fFocalLength)
// Special case: the EXIF 2.2 standard represents ISO speed ratings with 2 bytes,
// which cannot hold ISO speed ratings above 65535 (e.g., 102400). In these
// cases, we write the maximum representable ISO speed rating value in the EXIF
// tag, i.e., 65535.
, fISOSpeedRatings (tcISOSpeedRatings,
(uint16) Min_uint32 (65535,
exif.fISOSpeedRatings [0]))
, fSensitivityType (tcSensitivityType, (uint16) exif.fSensitivityType)
, fStandardOutputSensitivity (tcStandardOutputSensitivity, exif.fStandardOutputSensitivity)
, fRecommendedExposureIndex (tcRecommendedExposureIndex, exif.fRecommendedExposureIndex)
, fISOSpeed (tcISOSpeed, exif.fISOSpeed)
, fISOSpeedLatitudeyyy (tcISOSpeedLatitudeyyy, exif.fISOSpeedLatitudeyyy)
, fISOSpeedLatitudezzz (tcISOSpeedLatitudezzz, exif.fISOSpeedLatitudezzz)
, fFlash (tcFlash, (uint16) exif.fFlash)
, fExposureProgram (tcExposureProgram, (uint16) exif.fExposureProgram)
, fMeteringMode (tcMeteringMode, (uint16) exif.fMeteringMode)
, fLightSource (tcLightSource, (uint16) exif.fLightSource)
, fSensingMethod (tcSensingMethodExif, (uint16) exif.fSensingMethod)
, fFocalLength35mm (tcFocalLengthIn35mmFilm, (uint16) exif.fFocalLengthIn35mmFilm)
, fFileSourceData ((uint8) exif.fFileSource)
, fFileSource (tcFileSource, ttUndefined, 1, &fFileSourceData)
, fSceneTypeData ((uint8) exif.fSceneType)
, fSceneType (tcSceneType, ttUndefined, 1, &fSceneTypeData)
, fCFAPattern (tcCFAPatternExif,
exif.fCFARepeatPatternRows,
exif.fCFARepeatPatternCols,
&exif.fCFAPattern [0] [0])
, fCustomRendered (tcCustomRendered , (uint16) exif.fCustomRendered )
, fExposureMode (tcExposureMode , (uint16) exif.fExposureMode )
, fWhiteBalance (tcWhiteBalance , (uint16) exif.fWhiteBalance )
, fSceneCaptureType (tcSceneCaptureType , (uint16) exif.fSceneCaptureType )
, fGainControl (tcGainControl , (uint16) exif.fGainControl )
, fContrast (tcContrast , (uint16) exif.fContrast )
, fSaturation (tcSaturation , (uint16) exif.fSaturation )
, fSharpness (tcSharpness , (uint16) exif.fSharpness )
, fSubjectDistanceRange (tcSubjectDistanceRange, (uint16) exif.fSubjectDistanceRange)
, fDigitalZoomRatio (tcDigitalZoomRatio, exif.fDigitalZoomRatio)
, fExposureIndex (tcExposureIndexExif, exif.fExposureIndex)
, fImageNumber (tcImageNumber, exif.fImageNumber)
, fSelfTimerMode (tcSelfTimerMode, (uint16) exif.fSelfTimerMode)
, fBatteryLevelA (tcBatteryLevel, exif.fBatteryLevelA)
, fBatteryLevelR (tcBatteryLevel, exif.fBatteryLevelR)
, fFocalPlaneXResolution (tcFocalPlaneXResolutionExif, exif.fFocalPlaneXResolution)
, fFocalPlaneYResolution (tcFocalPlaneYResolutionExif, exif.fFocalPlaneYResolution)
, fFocalPlaneResolutionUnit (tcFocalPlaneResolutionUnitExif, (uint16) exif.fFocalPlaneResolutionUnit)
, fSubjectArea (tcSubjectArea, fSubjectAreaData, exif.fSubjectAreaCount)
, fLensInfo (tcLensInfo, fLensInfoData, 4)
, fDateTime (tcDateTime , exif.fDateTime .DateTime ())
, fDateTimeOriginal (tcDateTimeOriginal , exif.fDateTimeOriginal .DateTime ())
, fDateTimeDigitized (tcDateTimeDigitized, exif.fDateTimeDigitized.DateTime ())
, fSubsecTime (tcSubsecTime, exif.fDateTime .Subseconds ())
, fSubsecTimeOriginal (tcSubsecTimeOriginal, exif.fDateTimeOriginal .Subseconds ())
, fSubsecTimeDigitized (tcSubsecTimeDigitized, exif.fDateTimeDigitized.Subseconds ())
, fMake (tcMake, exif.fMake)
, fModel (tcModel, exif.fModel)
, fArtist (tcArtist, exif.fArtist)
, fSoftware (tcSoftware, exif.fSoftware)
, fCopyright (tcCopyright, exif.fCopyright)
, fMakerNoteSafety (tcMakerNoteSafety, makerNoteSafe ? 1 : 0)
, fMakerNote (tcMakerNote, ttUndefined, makerNoteLength, makerNoteData)
, fImageDescription (tcImageDescription, exif.fImageDescription)
, fSerialNumber (tcCameraSerialNumber, exif.fCameraSerialNumber)
, fUserComment (tcUserComment, exif.fUserComment)
, fImageUniqueID (tcImageUniqueID, ttAscii, 33, fImageUniqueIDData)
// EXIF 2.3 tags.
, fCameraOwnerName (tcCameraOwnerNameExif, exif.fOwnerName )
, fBodySerialNumber (tcCameraSerialNumberExif, exif.fCameraSerialNumber)
, fLensSpecification (tcLensSpecificationExif, fLensInfoData, 4 )
, fLensMake (tcLensMakeExif, exif.fLensMake )
, fLensModel (tcLensModelExif, exif.fLensName )
, fLensSerialNumber (tcLensSerialNumberExif, exif.fLensSerialNumber )
, fGPSVersionID (tcGPSVersionID, fGPSVersionData, 4)
, fGPSLatitudeRef (tcGPSLatitudeRef, exif.fGPSLatitudeRef)
, fGPSLatitude (tcGPSLatitude, exif.fGPSLatitude, 3)
, fGPSLongitudeRef (tcGPSLongitudeRef, exif.fGPSLongitudeRef)
, fGPSLongitude (tcGPSLongitude, exif.fGPSLongitude, 3)
, fGPSAltitudeRef (tcGPSAltitudeRef, (uint8) exif.fGPSAltitudeRef)
, fGPSAltitude (tcGPSAltitude, exif.fGPSAltitude )
, fGPSTimeStamp (tcGPSTimeStamp, exif.fGPSTimeStamp, 3)
, fGPSSatellites (tcGPSSatellites , exif.fGPSSatellites )
, fGPSStatus (tcGPSStatus , exif.fGPSStatus )
, fGPSMeasureMode (tcGPSMeasureMode, exif.fGPSMeasureMode)
, fGPSDOP (tcGPSDOP, exif.fGPSDOP)
, fGPSSpeedRef (tcGPSSpeedRef, exif.fGPSSpeedRef)
, fGPSSpeed (tcGPSSpeed , exif.fGPSSpeed )
, fGPSTrackRef (tcGPSTrackRef, exif.fGPSTrackRef)
, fGPSTrack (tcGPSTrack , exif.fGPSTrack )
, fGPSImgDirectionRef (tcGPSImgDirectionRef, exif.fGPSImgDirectionRef)
, fGPSImgDirection (tcGPSImgDirection , exif.fGPSImgDirection )
, fGPSMapDatum (tcGPSMapDatum, exif.fGPSMapDatum)
, fGPSDestLatitudeRef (tcGPSDestLatitudeRef, exif.fGPSDestLatitudeRef)
, fGPSDestLatitude (tcGPSDestLatitude, exif.fGPSDestLatitude, 3)
, fGPSDestLongitudeRef (tcGPSDestLongitudeRef, exif.fGPSDestLongitudeRef)
, fGPSDestLongitude (tcGPSDestLongitude, exif.fGPSDestLongitude, 3)
, fGPSDestBearingRef (tcGPSDestBearingRef, exif.fGPSDestBearingRef)
, fGPSDestBearing (tcGPSDestBearing , exif.fGPSDestBearing )
, fGPSDestDistanceRef (tcGPSDestDistanceRef, exif.fGPSDestDistanceRef)
, fGPSDestDistance (tcGPSDestDistance , exif.fGPSDestDistance )
, fGPSProcessingMethod (tcGPSProcessingMethod, exif.fGPSProcessingMethod)
, fGPSAreaInformation (tcGPSAreaInformation , exif.fGPSAreaInformation )
, fGPSDateStamp (tcGPSDateStamp, exif.fGPSDateStamp)
, fGPSDifferential (tcGPSDifferential, (uint16) exif.fGPSDifferential)
, fGPSHPositioningError (tcGPSHPositioningError, exif.fGPSHPositioningError)
{
if (exif.fExifVersion)
{
fExifVersionData [0] = (uint8) (exif.fExifVersion >> 24);
fExifVersionData [1] = (uint8) (exif.fExifVersion >> 16);
fExifVersionData [2] = (uint8) (exif.fExifVersion >> 8);
fExifVersionData [3] = (uint8) (exif.fExifVersion );
fExifIFD.Add (&fExifVersion);
}
if (exif.fExposureTime.IsValid ())
{
fExifIFD.Add (&fExposureTime);
}
if (exif.fShutterSpeedValue.IsValid ())
{
fExifIFD.Add (&fShutterSpeedValue);
}
if (exif.fFNumber.IsValid ())
{
fExifIFD.Add (&fFNumber);
}
if (exif.fApertureValue.IsValid ())
{
fExifIFD.Add (&fApertureValue);
}
if (exif.fBrightnessValue.IsValid ())
{
fExifIFD.Add (&fBrightnessValue);
}
if (exif.fExposureBiasValue.IsValid ())
{
fExifIFD.Add (&fExposureBiasValue);
}
if (exif.fMaxApertureValue.IsValid ())
{
fExifIFD.Add (&fMaxApertureValue);
}
if (exif.fSubjectDistance.IsValid ())
{
fExifIFD.Add (&fSubjectDistance);
}
if (exif.fFocalLength.IsValid ())
{
fExifIFD.Add (&fFocalLength);
}
if (exif.fISOSpeedRatings [0] != 0)
{
fExifIFD.Add (&fISOSpeedRatings);
}
if (exif.fFlash <= 0x0FFFF)
{
fExifIFD.Add (&fFlash);
}
if (exif.fExposureProgram <= 0x0FFFF)
{
fExifIFD.Add (&fExposureProgram);
}
if (exif.fMeteringMode <= 0x0FFFF)
{
fExifIFD.Add (&fMeteringMode);
}
if (exif.fLightSource <= 0x0FFFF)
{
fExifIFD.Add (&fLightSource);
}
if (exif.fSensingMethod <= 0x0FFFF)
{
fExifIFD.Add (&fSensingMethod);
}
if (exif.fFocalLengthIn35mmFilm != 0)
{
fExifIFD.Add (&fFocalLength35mm);
}
if (exif.fFileSource <= 0x0FF)
{
fExifIFD.Add (&fFileSource);
}
if (exif.fSceneType <= 0x0FF)
{
fExifIFD.Add (&fSceneType);
}
if (exif.fCFARepeatPatternRows &&
exif.fCFARepeatPatternCols)
{
fExifIFD.Add (&fCFAPattern);
}
if (exif.fCustomRendered <= 0x0FFFF)
{
fExifIFD.Add (&fCustomRendered);
}
if (exif.fExposureMode <= 0x0FFFF)
{
fExifIFD.Add (&fExposureMode);
}
if (exif.fWhiteBalance <= 0x0FFFF)
{
fExifIFD.Add (&fWhiteBalance);
}
if (exif.fSceneCaptureType <= 0x0FFFF)
{
fExifIFD.Add (&fSceneCaptureType);
}
if (exif.fGainControl <= 0x0FFFF)
{
fExifIFD.Add (&fGainControl);
}
if (exif.fContrast <= 0x0FFFF)
{
fExifIFD.Add (&fContrast);
}
if (exif.fSaturation <= 0x0FFFF)
{
fExifIFD.Add (&fSaturation);
}
if (exif.fSharpness <= 0x0FFFF)
{
fExifIFD.Add (&fSharpness);
}
if (exif.fSubjectDistanceRange <= 0x0FFFF)
{
fExifIFD.Add (&fSubjectDistanceRange);
}
if (exif.fDigitalZoomRatio.IsValid ())
{
fExifIFD.Add (&fDigitalZoomRatio);
}
if (exif.fExposureIndex.IsValid ())
{
fExifIFD.Add (&fExposureIndex);
}
if (insideDNG) // TIFF-EP only tags
{
if (exif.fImageNumber != 0xFFFFFFFF)
{
directory.Add (&fImageNumber);
}
if (exif.fSelfTimerMode <= 0x0FFFF)
{
directory.Add (&fSelfTimerMode);
}
if (exif.fBatteryLevelA.NotEmpty ())
{
directory.Add (&fBatteryLevelA);
}
else if (exif.fBatteryLevelR.IsValid ())
{
directory.Add (&fBatteryLevelR);
}
}
if (exif.fFocalPlaneXResolution.IsValid ())
{
fExifIFD.Add (&fFocalPlaneXResolution);
}
if (exif.fFocalPlaneYResolution.IsValid ())
{
fExifIFD.Add (&fFocalPlaneYResolution);
}
if (exif.fFocalPlaneResolutionUnit <= 0x0FFFF)
{
fExifIFD.Add (&fFocalPlaneResolutionUnit);
}
if (exif.fSubjectAreaCount)
{
fSubjectAreaData [0] = (uint16) exif.fSubjectArea [0];
fSubjectAreaData [1] = (uint16) exif.fSubjectArea [1];
fSubjectAreaData [2] = (uint16) exif.fSubjectArea [2];
fSubjectAreaData [3] = (uint16) exif.fSubjectArea [3];
fExifIFD.Add (&fSubjectArea);
}
if (exif.fLensInfo [0].IsValid () &&
exif.fLensInfo [1].IsValid ())
{
fLensInfoData [0] = exif.fLensInfo [0];
fLensInfoData [1] = exif.fLensInfo [1];
fLensInfoData [2] = exif.fLensInfo [2];
fLensInfoData [3] = exif.fLensInfo [3];
if (insideDNG)
{
directory.Add (&fLensInfo);
}
}
if (exif.fDateTime.IsValid ())
{
directory.Add (&fDateTime);
if (exif.fDateTime.Subseconds ().NotEmpty ())
{
fExifIFD.Add (&fSubsecTime);
}
}
if (exif.fDateTimeOriginal.IsValid ())
{
fExifIFD.Add (&fDateTimeOriginal);
if (exif.fDateTimeOriginal.Subseconds ().NotEmpty ())
{
fExifIFD.Add (&fSubsecTimeOriginal);
}
}
if (exif.fDateTimeDigitized.IsValid ())
{
fExifIFD.Add (&fDateTimeDigitized);
if (exif.fDateTimeDigitized.Subseconds ().NotEmpty ())
{
fExifIFD.Add (&fSubsecTimeDigitized);
}
}
if (exif.fMake.NotEmpty ())
{
directory.Add (&fMake);
}
if (exif.fModel.NotEmpty ())
{
directory.Add (&fModel);
}
if (exif.fArtist.NotEmpty ())
{
directory.Add (&fArtist);
}
if (exif.fSoftware.NotEmpty ())
{
directory.Add (&fSoftware);
}
if (exif.fCopyright.NotEmpty ())
{
directory.Add (&fCopyright);
}
if (exif.fImageDescription.NotEmpty ())
{
directory.Add (&fImageDescription);
}
if (exif.fCameraSerialNumber.NotEmpty () && insideDNG)
{
directory.Add (&fSerialNumber);
}
if (makerNoteSafe && makerNoteData)
{
directory.Add (&fMakerNoteSafety);
fExifIFD.Add (&fMakerNote);
}
if (exif.fUserComment.NotEmpty ())
{
fExifIFD.Add (&fUserComment);
}
if (exif.fImageUniqueID.IsValid ())
{
for (uint32 j = 0; j < 16; j++)
{
sprintf (fImageUniqueIDData + j * 2,
"%02X",
(unsigned) exif.fImageUniqueID.data [j]);
}
fExifIFD.Add (&fImageUniqueID);
}
if (exif.AtLeastVersion0230 ())
{
if (exif.fSensitivityType != 0)
{
fExifIFD.Add (&fSensitivityType);
}
// Sensitivity tags. Do not write these extra tags unless the SensitivityType
// and PhotographicSensitivity (i.e., ISOSpeedRatings) values are valid.
if (exif.fSensitivityType != 0 &&
exif.fISOSpeedRatings [0] != 0)
{
// Standard Output Sensitivity (SOS).
if (exif.fStandardOutputSensitivity != 0)
{
fExifIFD.Add (&fStandardOutputSensitivity);
}
// Recommended Exposure Index (REI).
if (exif.fRecommendedExposureIndex != 0)
{
fExifIFD.Add (&fRecommendedExposureIndex);
}
// ISO Speed.
if (exif.fISOSpeed != 0)
{
fExifIFD.Add (&fISOSpeed);
if (exif.fISOSpeedLatitudeyyy != 0 &&
exif.fISOSpeedLatitudezzz != 0)
{
fExifIFD.Add (&fISOSpeedLatitudeyyy);
fExifIFD.Add (&fISOSpeedLatitudezzz);
}
}
}
if (exif.fOwnerName.NotEmpty ())
{
fExifIFD.Add (&fCameraOwnerName);
}
if (exif.fCameraSerialNumber.NotEmpty ())
{
fExifIFD.Add (&fBodySerialNumber);
}
if (exif.fLensInfo [0].IsValid () &&
exif.fLensInfo [1].IsValid ())
{
fExifIFD.Add (&fLensSpecification);
}
if (exif.fLensMake.NotEmpty ())
{
fExifIFD.Add (&fLensMake);
}
if (exif.fLensName.NotEmpty ())
{
fExifIFD.Add (&fLensModel);
}
if (exif.fLensSerialNumber.NotEmpty ())
{
fExifIFD.Add (&fLensSerialNumber);
}
}
if (exif.fGPSVersionID)
{
fGPSVersionData [0] = (uint8) (exif.fGPSVersionID >> 24);
fGPSVersionData [1] = (uint8) (exif.fGPSVersionID >> 16);
fGPSVersionData [2] = (uint8) (exif.fGPSVersionID >> 8);
fGPSVersionData [3] = (uint8) (exif.fGPSVersionID );
fGPSIFD.Add (&fGPSVersionID);
}
if (exif.fGPSLatitudeRef.NotEmpty () &&
exif.fGPSLatitude [0].IsValid ())
{
fGPSIFD.Add (&fGPSLatitudeRef);
fGPSIFD.Add (&fGPSLatitude );
}
if (exif.fGPSLongitudeRef.NotEmpty () &&
exif.fGPSLongitude [0].IsValid ())
{
fGPSIFD.Add (&fGPSLongitudeRef);
fGPSIFD.Add (&fGPSLongitude );
}
if (exif.fGPSAltitudeRef <= 0x0FF)
{
fGPSIFD.Add (&fGPSAltitudeRef);
}
if (exif.fGPSAltitude.IsValid ())
{
fGPSIFD.Add (&fGPSAltitude);
}
if (exif.fGPSTimeStamp [0].IsValid ())
{
fGPSIFD.Add (&fGPSTimeStamp);
}
if (exif.fGPSSatellites.NotEmpty ())
{
fGPSIFD.Add (&fGPSSatellites);
}
if (exif.fGPSStatus.NotEmpty ())
{
fGPSIFD.Add (&fGPSStatus);
}
if (exif.fGPSMeasureMode.NotEmpty ())
{
fGPSIFD.Add (&fGPSMeasureMode);
}
if (exif.fGPSDOP.IsValid ())
{
fGPSIFD.Add (&fGPSDOP);
}
if (exif.fGPSSpeedRef.NotEmpty ())
{
fGPSIFD.Add (&fGPSSpeedRef);
}
if (exif.fGPSSpeed.IsValid ())
{
fGPSIFD.Add (&fGPSSpeed);
}
if (exif.fGPSTrackRef.NotEmpty ())
{
fGPSIFD.Add (&fGPSTrackRef);
}
if (exif.fGPSTrack.IsValid ())
{
fGPSIFD.Add (&fGPSTrack);
}
if (exif.fGPSImgDirectionRef.NotEmpty ())
{
fGPSIFD.Add (&fGPSImgDirectionRef);
}
if (exif.fGPSImgDirection.IsValid ())
{
fGPSIFD.Add (&fGPSImgDirection);
}
if (exif.fGPSMapDatum.NotEmpty ())
{
fGPSIFD.Add (&fGPSMapDatum);
}
if (exif.fGPSDestLatitudeRef.NotEmpty () &&
exif.fGPSDestLatitude [0].IsValid ())
{
fGPSIFD.Add (&fGPSDestLatitudeRef);
fGPSIFD.Add (&fGPSDestLatitude );
}
if (exif.fGPSDestLongitudeRef.NotEmpty () &&
exif.fGPSDestLongitude [0].IsValid ())
{
fGPSIFD.Add (&fGPSDestLongitudeRef);
fGPSIFD.Add (&fGPSDestLongitude );
}
if (exif.fGPSDestBearingRef.NotEmpty ())
{
fGPSIFD.Add (&fGPSDestBearingRef);
}
if (exif.fGPSDestBearing.IsValid ())
{
fGPSIFD.Add (&fGPSDestBearing);
}
if (exif.fGPSDestDistanceRef.NotEmpty ())
{
fGPSIFD.Add (&fGPSDestDistanceRef);
}
if (exif.fGPSDestDistance.IsValid ())
{
fGPSIFD.Add (&fGPSDestDistance);
}
if (exif.fGPSProcessingMethod.NotEmpty ())
{
fGPSIFD.Add (&fGPSProcessingMethod);
}
if (exif.fGPSAreaInformation.NotEmpty ())
{
fGPSIFD.Add (&fGPSAreaInformation);
}
if (exif.fGPSDateStamp.NotEmpty ())
{
fGPSIFD.Add (&fGPSDateStamp);
}
if (exif.fGPSDifferential <= 0x0FFFF)
{
fGPSIFD.Add (&fGPSDifferential);
}
if (exif.AtLeastVersion0230 ())
{
if (exif.fGPSHPositioningError.IsValid ())
{
fGPSIFD.Add (&fGPSHPositioningError);
}
}
AddLinks (directory);
}
/******************************************************************************/
void exif_tag_set::AddLinks (dng_tiff_directory &directory)
{
if (fExifIFD.Size () != 0 && !fAddedExifLink)
{
directory.Add (&fExifLink);
fAddedExifLink = true;
}
if (fGPSIFD.Size () != 0 && !fAddedGPSLink)
{
directory.Add (&fGPSLink);
fAddedGPSLink = true;
}
}
/******************************************************************************/
class range_tag_set
{
private:
uint32 fActiveAreaData [4];
tag_uint32_ptr fActiveArea;
uint32 fMaskedAreaData [kMaxMaskedAreas * 4];
tag_uint32_ptr fMaskedAreas;
tag_uint16_ptr fLinearizationTable;
uint16 fBlackLevelRepeatDimData [2];
tag_uint16_ptr fBlackLevelRepeatDim;
dng_urational fBlackLevelData [kMaxBlackPattern *
kMaxBlackPattern *
kMaxSamplesPerPixel];
tag_urational_ptr fBlackLevel;
dng_memory_data fBlackLevelDeltaHData;
dng_memory_data fBlackLevelDeltaVData;
tag_srational_ptr fBlackLevelDeltaH;
tag_srational_ptr fBlackLevelDeltaV;
uint16 fWhiteLevelData16 [kMaxSamplesPerPixel];
uint32 fWhiteLevelData32 [kMaxSamplesPerPixel];
tag_uint16_ptr fWhiteLevel16;
tag_uint32_ptr fWhiteLevel32;
public:
range_tag_set (dng_tiff_directory &directory,
const dng_negative &negative);
};
/******************************************************************************/
range_tag_set::range_tag_set (dng_tiff_directory &directory,
const dng_negative &negative)
: fActiveArea (tcActiveArea,
fActiveAreaData,
4)
, fMaskedAreas (tcMaskedAreas,
fMaskedAreaData,
0)
, fLinearizationTable (tcLinearizationTable,
NULL,
0)
, fBlackLevelRepeatDim (tcBlackLevelRepeatDim,
fBlackLevelRepeatDimData,
2)
, fBlackLevel (tcBlackLevel,
fBlackLevelData)
, fBlackLevelDeltaHData ()
, fBlackLevelDeltaVData ()
, fBlackLevelDeltaH (tcBlackLevelDeltaH)
, fBlackLevelDeltaV (tcBlackLevelDeltaV)
, fWhiteLevel16 (tcWhiteLevel,
fWhiteLevelData16)
, fWhiteLevel32 (tcWhiteLevel,
fWhiteLevelData32)
{
const dng_image &rawImage (negative.RawImage ());
const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo ();
if (rangeInfo)
{
// ActiveArea:
{
const dng_rect &r = rangeInfo->fActiveArea;
if (r.NotEmpty ())
{
fActiveAreaData [0] = r.t;
fActiveAreaData [1] = r.l;
fActiveAreaData [2] = r.b;
fActiveAreaData [3] = r.r;
directory.Add (&fActiveArea);
}
}
// MaskedAreas:
if (rangeInfo->fMaskedAreaCount)
{
fMaskedAreas.SetCount (rangeInfo->fMaskedAreaCount * 4);
for (uint32 index = 0; index < rangeInfo->fMaskedAreaCount; index++)
{
const dng_rect &r = rangeInfo->fMaskedArea [index];
fMaskedAreaData [index * 4 + 0] = r.t;
fMaskedAreaData [index * 4 + 1] = r.l;
fMaskedAreaData [index * 4 + 2] = r.b;
fMaskedAreaData [index * 4 + 3] = r.r;
}
directory.Add (&fMaskedAreas);
}
// LinearizationTable:
if (rangeInfo->fLinearizationTable.Get ())
{
fLinearizationTable.SetData (rangeInfo->fLinearizationTable->Buffer_uint16 () );
fLinearizationTable.SetCount (rangeInfo->fLinearizationTable->LogicalSize () >> 1);
directory.Add (&fLinearizationTable);
}
// BlackLevelRepeatDim:
{
fBlackLevelRepeatDimData [0] = (uint16) rangeInfo->fBlackLevelRepeatRows;
fBlackLevelRepeatDimData [1] = (uint16) rangeInfo->fBlackLevelRepeatCols;
directory.Add (&fBlackLevelRepeatDim);
}
// BlackLevel:
{
uint32 index = 0;
for (uint16 v = 0; v < rangeInfo->fBlackLevelRepeatRows; v++)
{
for (uint32 h = 0; h < rangeInfo->fBlackLevelRepeatCols; h++)
{
for (uint32 c = 0; c < rawImage.Planes (); c++)
{
fBlackLevelData [index++] = rangeInfo->BlackLevel (v, h, c);
}
}
}
fBlackLevel.SetCount (rangeInfo->fBlackLevelRepeatRows *
rangeInfo->fBlackLevelRepeatCols * rawImage.Planes ());
directory.Add (&fBlackLevel);
}
// BlackLevelDeltaH:
if (rangeInfo->ColumnBlackCount ())
{
uint32 count = rangeInfo->ColumnBlackCount ();
fBlackLevelDeltaHData.Allocate (count, sizeof (dng_srational));
dng_srational *blacks = (dng_srational *) fBlackLevelDeltaHData.Buffer ();
for (uint32 col = 0; col < count; col++)
{
blacks [col] = rangeInfo->ColumnBlack (col);
}
fBlackLevelDeltaH.SetData (blacks);
fBlackLevelDeltaH.SetCount (count );
directory.Add (&fBlackLevelDeltaH);
}
// BlackLevelDeltaV:
if (rangeInfo->RowBlackCount ())
{
uint32 count = rangeInfo->RowBlackCount ();
fBlackLevelDeltaVData.Allocate (count, sizeof (dng_srational));
dng_srational *blacks = (dng_srational *) fBlackLevelDeltaVData.Buffer ();
for (uint32 row = 0; row < count; row++)
{
blacks [row] = rangeInfo->RowBlack (row);
}
fBlackLevelDeltaV.SetData (blacks);
fBlackLevelDeltaV.SetCount (count );
directory.Add (&fBlackLevelDeltaV);
}
}
// WhiteLevel:
// Only use the 32-bit data type if we must use it since there
// are some lazy (non-Adobe) DNG readers out there.
bool needs32 = false;
fWhiteLevel16.SetCount (rawImage.Planes ());
fWhiteLevel32.SetCount (rawImage.Planes ());
for (uint32 c = 0; c < fWhiteLevel16.Count (); c++)
{
fWhiteLevelData32 [c] = negative.WhiteLevel (c);
if (fWhiteLevelData32 [c] > 0x0FFFF)
{
needs32 = true;
}
fWhiteLevelData16 [c] = (uint16) fWhiteLevelData32 [c];
}
if (needs32)
{
directory.Add (&fWhiteLevel32);
}
else
{
directory.Add (&fWhiteLevel16);
}
}
/******************************************************************************/
class mosaic_tag_set
{
private:
uint16 fCFARepeatPatternDimData [2];
tag_uint16_ptr fCFARepeatPatternDim;
uint8 fCFAPatternData [kMaxCFAPattern *
kMaxCFAPattern];
tag_uint8_ptr fCFAPattern;
uint8 fCFAPlaneColorData [kMaxColorPlanes];
tag_uint8_ptr fCFAPlaneColor;
tag_uint16 fCFALayout;
tag_uint32 fGreenSplit;
public:
mosaic_tag_set (dng_tiff_directory &directory,
const dng_mosaic_info &info);
};
/******************************************************************************/
mosaic_tag_set::mosaic_tag_set (dng_tiff_directory &directory,
const dng_mosaic_info &info)
: fCFARepeatPatternDim (tcCFARepeatPatternDim,
fCFARepeatPatternDimData,
2)
, fCFAPattern (tcCFAPattern,
fCFAPatternData)
, fCFAPlaneColor (tcCFAPlaneColor,
fCFAPlaneColorData)
, fCFALayout (tcCFALayout,
(uint16) info.fCFALayout)
, fGreenSplit (tcBayerGreenSplit,
info.fBayerGreenSplit)
{
if (info.IsColorFilterArray ())
{
// CFARepeatPatternDim:
fCFARepeatPatternDimData [0] = (uint16) info.fCFAPatternSize.v;
fCFARepeatPatternDimData [1] = (uint16) info.fCFAPatternSize.h;
directory.Add (&fCFARepeatPatternDim);
// CFAPattern:
fCFAPattern.SetCount (info.fCFAPatternSize.v *
info.fCFAPatternSize.h);
for (int32 r = 0; r < info.fCFAPatternSize.v; r++)
{
for (int32 c = 0; c < info.fCFAPatternSize.h; c++)
{
fCFAPatternData [r * info.fCFAPatternSize.h + c] = info.fCFAPattern [r] [c];
}
}
directory.Add (&fCFAPattern);
// CFAPlaneColor:
fCFAPlaneColor.SetCount (info.fColorPlanes);
for (uint32 j = 0; j < info.fColorPlanes; j++)
{
fCFAPlaneColorData [j] = info.fCFAPlaneColor [j];
}
directory.Add (&fCFAPlaneColor);
// CFALayout:
fCFALayout.Set ((uint16) info.fCFALayout);
directory.Add (&fCFALayout);
// BayerGreenSplit: (only include if the pattern is a Bayer pattern)
if (info.fCFAPatternSize == dng_point (2, 2) &&
info.fColorPlanes == 3)
{
directory.Add (&fGreenSplit);
}
}
}
/******************************************************************************/
class color_tag_set
{
private:
uint32 fColorChannels;
tag_matrix fCameraCalibration1;
tag_matrix fCameraCalibration2;
tag_string fCameraCalibrationSignature;
tag_string fAsShotProfileName;
dng_urational fAnalogBalanceData [4];
tag_urational_ptr fAnalogBalance;
dng_urational fAsShotNeutralData [4];
tag_urational_ptr fAsShotNeutral;
dng_urational fAsShotWhiteXYData [2];
tag_urational_ptr fAsShotWhiteXY;
tag_urational fLinearResponseLimit;
public:
color_tag_set (dng_tiff_directory &directory,
const dng_negative &negative);
};
/******************************************************************************/
color_tag_set::color_tag_set (dng_tiff_directory &directory,
const dng_negative &negative)
: fColorChannels (negative.ColorChannels ())
, fCameraCalibration1 (tcCameraCalibration1,
negative.CameraCalibration1 ())
, fCameraCalibration2 (tcCameraCalibration2,
negative.CameraCalibration2 ())
, fCameraCalibrationSignature (tcCameraCalibrationSignature,
negative.CameraCalibrationSignature ())
, fAsShotProfileName (tcAsShotProfileName,
negative.AsShotProfileName ())
, fAnalogBalance (tcAnalogBalance,
fAnalogBalanceData,
fColorChannels)
, fAsShotNeutral (tcAsShotNeutral,
fAsShotNeutralData,
fColorChannels)
, fAsShotWhiteXY (tcAsShotWhiteXY,
fAsShotWhiteXYData,
2)
, fLinearResponseLimit (tcLinearResponseLimit,
negative.LinearResponseLimitR ())
{
if (fColorChannels > 1)
{
uint32 channels2 = fColorChannels * fColorChannels;
if (fCameraCalibration1.Count () == channels2)
{
directory.Add (&fCameraCalibration1);
}
if (fCameraCalibration2.Count () == channels2)
{
directory.Add (&fCameraCalibration2);
}
if (fCameraCalibration1.Count () == channels2 ||
fCameraCalibration2.Count () == channels2)
{
if (negative.CameraCalibrationSignature ().NotEmpty ())
{
directory.Add (&fCameraCalibrationSignature);
}
}
if (negative.AsShotProfileName ().NotEmpty ())
{
directory.Add (&fAsShotProfileName);
}
for (uint32 j = 0; j < fColorChannels; j++)
{
fAnalogBalanceData [j] = negative.AnalogBalanceR (j);
}
directory.Add (&fAnalogBalance);
if (negative.HasCameraNeutral ())
{
for (uint32 k = 0; k < fColorChannels; k++)
{
fAsShotNeutralData [k] = negative.CameraNeutralR (k);
}
directory.Add (&fAsShotNeutral);
}
else if (negative.HasCameraWhiteXY ())
{
negative.GetCameraWhiteXY (fAsShotWhiteXYData [0],
fAsShotWhiteXYData [1]);
directory.Add (&fAsShotWhiteXY);
}
directory.Add (&fLinearResponseLimit);
}
}
/******************************************************************************/
class profile_tag_set
{
private:
tag_uint16 fCalibrationIlluminant1;
tag_uint16 fCalibrationIlluminant2;
tag_matrix fColorMatrix1;
tag_matrix fColorMatrix2;
tag_matrix fForwardMatrix1;
tag_matrix fForwardMatrix2;
tag_matrix fReductionMatrix1;
tag_matrix fReductionMatrix2;
tag_string fProfileName;
tag_string fProfileCalibrationSignature;
tag_uint32 fEmbedPolicyTag;
tag_string fCopyrightTag;
uint32 fHueSatMapDimData [3];
tag_uint32_ptr fHueSatMapDims;
tag_data_ptr fHueSatData1;
tag_data_ptr fHueSatData2;
tag_uint32 fHueSatMapEncodingTag;
uint32 fLookTableDimData [3];
tag_uint32_ptr fLookTableDims;
tag_data_ptr fLookTableData;
tag_uint32 fLookTableEncodingTag;
tag_srational fBaselineExposureOffsetTag;
tag_uint32 fDefaultBlackRenderTag;
dng_memory_data fToneCurveBuffer;
tag_data_ptr fToneCurveTag;
public:
profile_tag_set (dng_tiff_directory &directory,
const dng_camera_profile &profile);
};
/******************************************************************************/
profile_tag_set::profile_tag_set (dng_tiff_directory &directory,
const dng_camera_profile &profile)
: fCalibrationIlluminant1 (tcCalibrationIlluminant1,
(uint16) profile.CalibrationIlluminant1 ())
, fCalibrationIlluminant2 (tcCalibrationIlluminant2,
(uint16) profile.CalibrationIlluminant2 ())
, fColorMatrix1 (tcColorMatrix1,
profile.ColorMatrix1 ())
, fColorMatrix2 (tcColorMatrix2,
profile.ColorMatrix2 ())
, fForwardMatrix1 (tcForwardMatrix1,
profile.ForwardMatrix1 ())
, fForwardMatrix2 (tcForwardMatrix2,
profile.ForwardMatrix2 ())
, fReductionMatrix1 (tcReductionMatrix1,
profile.ReductionMatrix1 ())
, fReductionMatrix2 (tcReductionMatrix2,
profile.ReductionMatrix2 ())
, fProfileName (tcProfileName,
profile.Name (),
false)
, fProfileCalibrationSignature (tcProfileCalibrationSignature,
profile.ProfileCalibrationSignature (),
false)
, fEmbedPolicyTag (tcProfileEmbedPolicy,
profile.EmbedPolicy ())
, fCopyrightTag (tcProfileCopyright,
profile.Copyright (),
false)
, fHueSatMapDims (tcProfileHueSatMapDims,
fHueSatMapDimData,
3)
, fHueSatData1 (tcProfileHueSatMapData1,
ttFloat,
profile.HueSatDeltas1 ().DeltasCount () * 3,
profile.HueSatDeltas1 ().GetConstDeltas ())
, fHueSatData2 (tcProfileHueSatMapData2,
ttFloat,
profile.HueSatDeltas2 ().DeltasCount () * 3,
profile.HueSatDeltas2 ().GetConstDeltas ())
, fHueSatMapEncodingTag (tcProfileHueSatMapEncoding,
profile.HueSatMapEncoding ())
, fLookTableDims (tcProfileLookTableDims,
fLookTableDimData,
3)
, fLookTableData (tcProfileLookTableData,
ttFloat,
profile.LookTable ().DeltasCount () * 3,
profile.LookTable ().GetConstDeltas ())
, fLookTableEncodingTag (tcProfileLookTableEncoding,
profile.LookTableEncoding ())
, fBaselineExposureOffsetTag (tcBaselineExposureOffset,
profile.BaselineExposureOffset ())
, fDefaultBlackRenderTag (tcDefaultBlackRender,
profile.DefaultBlackRender ())
, fToneCurveBuffer ()
, fToneCurveTag (tcProfileToneCurve,
ttFloat,
0,
NULL)
{
if (profile.HasColorMatrix1 ())
{
uint32 colorChannels = profile.ColorMatrix1 ().Rows ();
directory.Add (&fCalibrationIlluminant1);
directory.Add (&fColorMatrix1);
if (fForwardMatrix1.Count () == colorChannels * 3)
{
directory.Add (&fForwardMatrix1);
}
if (colorChannels > 3 && fReductionMatrix1.Count () == colorChannels * 3)
{
directory.Add (&fReductionMatrix1);
}
if (profile.HasColorMatrix2 ())
{
directory.Add (&fCalibrationIlluminant2);
directory.Add (&fColorMatrix2);
if (fForwardMatrix2.Count () == colorChannels * 3)
{
directory.Add (&fForwardMatrix2);
}
if (colorChannels > 3 && fReductionMatrix2.Count () == colorChannels * 3)
{
directory.Add (&fReductionMatrix2);
}
}
if (profile.Name ().NotEmpty ())
{
directory.Add (&fProfileName);
}
if (profile.ProfileCalibrationSignature ().NotEmpty ())
{
directory.Add (&fProfileCalibrationSignature);
}
directory.Add (&fEmbedPolicyTag);
if (profile.Copyright ().NotEmpty ())
{
directory.Add (&fCopyrightTag);
}
bool haveHueSat1 = profile.HueSatDeltas1 ().IsValid ();
bool haveHueSat2 = profile.HueSatDeltas2 ().IsValid () &&
profile.HasColorMatrix2 ();
if (haveHueSat1 || haveHueSat2)
{
uint32 hueDivs = 0;
uint32 satDivs = 0;
uint32 valDivs = 0;
if (haveHueSat1)
{
profile.HueSatDeltas1 ().GetDivisions (hueDivs,
satDivs,
valDivs);
}
else
{
profile.HueSatDeltas2 ().GetDivisions (hueDivs,
satDivs,
valDivs);
}
fHueSatMapDimData [0] = hueDivs;
fHueSatMapDimData [1] = satDivs;
fHueSatMapDimData [2] = valDivs;
directory.Add (&fHueSatMapDims);
// Don't bother including the ProfileHueSatMapEncoding tag unless it's
// non-linear.
if (profile.HueSatMapEncoding () != encoding_Linear)
{
directory.Add (&fHueSatMapEncodingTag);
}
}
if (haveHueSat1)
{
directory.Add (&fHueSatData1);
}
if (haveHueSat2)
{
directory.Add (&fHueSatData2);
}
if (profile.HasLookTable ())
{
uint32 hueDivs = 0;
uint32 satDivs = 0;
uint32 valDivs = 0;
profile.LookTable ().GetDivisions (hueDivs,
satDivs,
valDivs);
fLookTableDimData [0] = hueDivs;
fLookTableDimData [1] = satDivs;
fLookTableDimData [2] = valDivs;
directory.Add (&fLookTableDims);
directory.Add (&fLookTableData);
// Don't bother including the ProfileLookTableEncoding tag unless it's
// non-linear.
if (profile.LookTableEncoding () != encoding_Linear)
{
directory.Add (&fLookTableEncodingTag);
}
}
// Don't bother including the BaselineExposureOffset tag unless it's both
// valid and non-zero.
if (profile.BaselineExposureOffset ().IsValid ())
{
if (profile.BaselineExposureOffset ().As_real64 () != 0.0)
{
directory.Add (&fBaselineExposureOffsetTag);
}
}
if (profile.DefaultBlackRender () != defaultBlackRender_Auto)
{
directory.Add (&fDefaultBlackRenderTag);
}
if (profile.ToneCurve ().IsValid ())
{
// Tone curve stored as pairs of 32-bit coordinates. Probably could do with
// 16-bits here, but should be small number of points so...
uint32 toneCurvePoints = (uint32) (profile.ToneCurve ().fCoord.size ());
fToneCurveBuffer.Allocate (SafeUint32Mult(toneCurvePoints, 2),
sizeof (real32));
real32 *points = fToneCurveBuffer.Buffer_real32 ();
fToneCurveTag.SetCount (toneCurvePoints * 2);
fToneCurveTag.SetData (points);
for (uint32 i = 0; i < toneCurvePoints; i++)
{
// Transpose coordinates so they are in a more expected
// order (domain -> range).
points [i * 2 ] = (real32) profile.ToneCurve ().fCoord [i].h;
points [i * 2 + 1] = (real32) profile.ToneCurve ().fCoord [i].v;
}
directory.Add (&fToneCurveTag);
}
}
}
/******************************************************************************/
tiff_dng_extended_color_profile::tiff_dng_extended_color_profile
(const dng_camera_profile &profile)
: fProfile (profile)
{
}
/******************************************************************************/
void tiff_dng_extended_color_profile::Put (dng_stream &stream,
bool includeModelRestriction)
{
// Profile header.
stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII);
stream.Put_uint16 (magicExtendedProfile);
stream.Put_uint32 (8);
// Profile tags.
profile_tag_set tagSet (*this, fProfile);
// Camera this profile is for.
tag_string cameraModelTag (tcUniqueCameraModel,
fProfile.UniqueCameraModelRestriction ());
if (includeModelRestriction)
{
if (fProfile.UniqueCameraModelRestriction ().NotEmpty ())
{
Add (&cameraModelTag);
}
}
// Write it all out.
dng_tiff_directory::Put (stream, offsetsRelativeToExplicitBase, 8);
}
/*****************************************************************************/
tag_dng_noise_profile::tag_dng_noise_profile (const dng_noise_profile &profile)
: tag_data_ptr (tcNoiseProfile,
ttDouble,
2 * profile.NumFunctions (),
fValues)
{
DNG_REQUIRE (profile.NumFunctions () <= kMaxColorPlanes,
"Too many noise functions in tag_dng_noise_profile.");
for (uint32 i = 0; i < profile.NumFunctions (); i++)
{
fValues [(2 * i) ] = profile.NoiseFunction (i).Scale ();
fValues [(2 * i) + 1] = profile.NoiseFunction (i).Offset ();
}
}
/*****************************************************************************/
dng_image_writer::dng_image_writer ()
{
}
/*****************************************************************************/
dng_image_writer::~dng_image_writer ()
{
}
/*****************************************************************************/
uint32 dng_image_writer::CompressedBufferSize (const dng_ifd &ifd,
uint32 uncompressedSize)
{
switch (ifd.fCompression)
{
case ccLZW:
{
// Add lots of slop for LZW to expand data.
return SafeUint32Add (SafeUint32Mult (uncompressedSize, 2), 1024);
}
case ccDeflate:
{
// ZLib says maximum is source size + 0.1% + 12 bytes.
return SafeUint32Add (SafeUint32Add (uncompressedSize,
uncompressedSize >> 8), 64);
}
case ccJPEG:
{
// If we are saving lossless JPEG from an 8-bit image, reserve
// space to pad the data out to 16-bits.
if (ifd.fBitsPerSample [0] <= 8)
{
return SafeUint32Mult (uncompressedSize, 2);
}
break;
}
default:
break;
}
return 0;
}
/******************************************************************************/
static void EncodeDelta8 (uint8 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = cols - 1; col > 0; col--)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/******************************************************************************/
static void EncodeDelta16 (uint16 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = cols - 1; col > 0; col--)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/******************************************************************************/
static void EncodeDelta32 (uint32 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = cols - 1; col > 0; col--)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/*****************************************************************************/
inline void EncodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels)
{
if (channels == 1)
{
bytePtr += (cols - 1);
uint8 this0 = bytePtr [0];
for (int32 col = 1; col < cols; col++)
{
uint8 prev0 = bytePtr [-1];
this0 -= prev0;
bytePtr [0] = this0;
this0 = prev0;
bytePtr -= 1;
}
}
else if (channels == 3)
{
bytePtr += (cols - 1) * 3;
uint8 this0 = bytePtr [0];
uint8 this1 = bytePtr [1];
uint8 this2 = bytePtr [2];
for (int32 col = 1; col < cols; col++)
{
uint8 prev0 = bytePtr [-3];
uint8 prev1 = bytePtr [-2];
uint8 prev2 = bytePtr [-1];
this0 -= prev0;
this1 -= prev1;
this2 -= prev2;
bytePtr [0] = this0;
bytePtr [1] = this1;
bytePtr [2] = this2;
this0 = prev0;
this1 = prev1;
this2 = prev2;
bytePtr -= 3;
}
}
else
{
uint32 rowBytes = cols * channels;
bytePtr += rowBytes - 1;
for (uint32 col = channels; col < rowBytes; col++)
{
bytePtr [0] -= bytePtr [-channels];
bytePtr--;
}
}
}
/*****************************************************************************/
static void EncodeFPDelta (uint8 *buffer,
uint8 *temp,
int32 cols,
int32 channels,
int32 bytesPerSample)
{
int32 rowIncrement = cols * channels;
if (bytesPerSample == 2)
{
const uint8 *src = buffer;
#if qDNGBigEndian
uint8 *dst0 = temp;
uint8 *dst1 = temp + rowIncrement;
#else
uint8 *dst1 = temp;
uint8 *dst0 = temp + rowIncrement;
#endif
for (int32 col = 0; col < rowIncrement; ++col)
{
dst0 [col] = src [0];
dst1 [col] = src [1];
src += 2;
}
}
else if (bytesPerSample == 3)
{
const uint8 *src = buffer;
uint8 *dst0 = temp;
uint8 *dst1 = temp + rowIncrement;
uint8 *dst2 = temp + rowIncrement * 2;
for (int32 col = 0; col < rowIncrement; ++col)
{
dst0 [col] = src [0];
dst1 [col] = src [1];
dst2 [col] = src [2];
src += 3;
}
}
else
{
const uint8 *src = buffer;
#if qDNGBigEndian
uint8 *dst0 = temp;
uint8 *dst1 = temp + rowIncrement;
uint8 *dst2 = temp + rowIncrement * 2;
uint8 *dst3 = temp + rowIncrement * 3;
#else
uint8 *dst3 = temp;
uint8 *dst2 = temp + rowIncrement;
uint8 *dst1 =