blob: 1f7d6add132dc951060b419508c40f94efb7dd79 [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_negative.cpp#3 $ */
/* $DateTime: 2012/06/14 20:24:41 $ */
/* $Change: 835078 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_negative.h"
#include "dng_1d_table.h"
#include "dng_abort_sniffer.h"
#include "dng_area_task.h"
#include "dng_assertions.h"
#include "dng_bottlenecks.h"
#include "dng_camera_profile.h"
#include "dng_color_space.h"
#include "dng_color_spec.h"
#include "dng_exceptions.h"
#include "dng_globals.h"
#include "dng_host.h"
#include "dng_image.h"
#include "dng_image_writer.h"
#include "dng_info.h"
#include "dng_jpeg_image.h"
#include "dng_linearization_info.h"
#include "dng_memory.h"
#include "dng_memory_stream.h"
#include "dng_misc_opcodes.h"
#include "dng_mosaic_info.h"
#include "dng_preview.h"
#include "dng_resample.h"
#include "dng_safe_arithmetic.h"
#include "dng_simple_image.h"
#include "dng_tag_codes.h"
#include "dng_tag_values.h"
#include "dng_tile_iterator.h"
#include "dng_utils.h"
#if qDNGUseXMP
#include "dng_xmp.h"
#endif
/*****************************************************************************/
dng_noise_profile::dng_noise_profile ()
: fNoiseFunctions ()
{
}
/*****************************************************************************/
dng_noise_profile::dng_noise_profile (const dng_std_vector<dng_noise_function> &functions)
: fNoiseFunctions (functions)
{
}
/*****************************************************************************/
bool dng_noise_profile::IsValid () const
{
if (NumFunctions () == 0 || NumFunctions () > kMaxColorPlanes)
{
return false;
}
for (uint32 plane = 0; plane < NumFunctions (); plane++)
{
if (!NoiseFunction (plane).IsValid ())
{
return false;
}
}
return true;
}
/*****************************************************************************/
bool dng_noise_profile::IsValidForNegative (const dng_negative &negative) const
{
if (!(NumFunctions () == 1 || NumFunctions () == negative.ColorChannels ()))
{
return false;
}
return IsValid ();
}
/*****************************************************************************/
const dng_noise_function & dng_noise_profile::NoiseFunction (uint32 plane) const
{
if (NumFunctions () == 1)
{
return fNoiseFunctions.front ();
}
DNG_REQUIRE (plane < NumFunctions (),
"Bad plane index argument for NoiseFunction ().");
return fNoiseFunctions [plane];
}
/*****************************************************************************/
uint32 dng_noise_profile::NumFunctions () const
{
return (uint32) fNoiseFunctions.size ();
}
/*****************************************************************************/
dng_metadata::dng_metadata (dng_host &host)
: fHasBaseOrientation (false)
, fBaseOrientation ()
, fIsMakerNoteSafe (false)
, fMakerNote ()
, fExif (host.Make_dng_exif ())
, fOriginalExif ()
, fIPTCBlock ()
, fIPTCOffset (kDNGStreamInvalidOffset)
#if qDNGUseXMP
, fXMP (host.Make_dng_xmp ())
#endif
, fEmbeddedXMPDigest ()
, fXMPinSidecar (false)
, fXMPisNewer (false)
, fSourceMIMI ()
{
}
/*****************************************************************************/
dng_metadata::~dng_metadata ()
{
}
/******************************************************************************/
template< class T >
T * CloneAutoPtr (const AutoPtr< T > &ptr)
{
return ptr.Get () ? ptr->Clone () : NULL;
}
/******************************************************************************/
template< class T, typename U >
T * CloneAutoPtr (const AutoPtr< T > &ptr, U &u)
{
return ptr.Get () ? ptr->Clone (u) : NULL;
}
/******************************************************************************/
dng_metadata::dng_metadata (const dng_metadata &rhs,
dng_memory_allocator &allocator)
: fHasBaseOrientation (rhs.fHasBaseOrientation)
, fBaseOrientation (rhs.fBaseOrientation)
, fIsMakerNoteSafe (rhs.fIsMakerNoteSafe)
, fMakerNote (CloneAutoPtr (rhs.fMakerNote, allocator))
, fExif (CloneAutoPtr (rhs.fExif))
, fOriginalExif (CloneAutoPtr (rhs.fOriginalExif))
, fIPTCBlock (CloneAutoPtr (rhs.fIPTCBlock, allocator))
, fIPTCOffset (rhs.fIPTCOffset)
#if qDNGUseXMP
, fXMP (CloneAutoPtr (rhs.fXMP))
#endif
, fEmbeddedXMPDigest (rhs.fEmbeddedXMPDigest)
, fXMPinSidecar (rhs.fXMPinSidecar)
, fXMPisNewer (rhs.fXMPisNewer)
, fSourceMIMI (rhs.fSourceMIMI)
{
}
/******************************************************************************/
dng_metadata * dng_metadata::Clone (dng_memory_allocator &allocator) const
{
return new dng_metadata (*this, allocator);
}
/******************************************************************************/
void dng_metadata::SetBaseOrientation (const dng_orientation &orientation)
{
fHasBaseOrientation = true;
fBaseOrientation = orientation;
}
/******************************************************************************/
void dng_metadata::ApplyOrientation (const dng_orientation &orientation)
{
fBaseOrientation += orientation;
#if qDNGUseXMP
fXMP->SetOrientation (fBaseOrientation);
#endif
}
/*****************************************************************************/
void dng_metadata::ResetExif (dng_exif * newExif)
{
fExif.Reset (newExif);
}
/******************************************************************************/
dng_memory_block * dng_metadata::BuildExifBlock (dng_memory_allocator &allocator,
const dng_resolution *resolution,
bool includeIPTC,
const dng_jpeg_preview *thumbnail) const
{
dng_memory_stream stream (allocator);
{
// Create the main IFD
dng_tiff_directory mainIFD;
// Optionally include the resolution tags.
dng_resolution res;
if (resolution)
{
res = *resolution;
}
tag_urational tagXResolution (tcXResolution, res.fXResolution);
tag_urational tagYResolution (tcYResolution, res.fYResolution);
tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit);
if (resolution)
{
mainIFD.Add (&tagXResolution );
mainIFD.Add (&tagYResolution );
mainIFD.Add (&tagResolutionUnit);
}
// Optionally include IPTC block.
tag_iptc tagIPTC (IPTCData (),
IPTCLength ());
if (includeIPTC && tagIPTC.Count ())
{
mainIFD.Add (&tagIPTC);
}
// Exif tags.
exif_tag_set exifSet (mainIFD,
*GetExif (),
IsMakerNoteSafe (),
MakerNoteData (),
MakerNoteLength (),
false);
// Figure out the Exif IFD offset.
uint32 exifOffset = 8 + mainIFD.Size ();
exifSet.Locate (exifOffset);
// Thumbnail IFD (if any).
dng_tiff_directory thumbIFD;
tag_uint16 thumbCompression (tcCompression, ccOldJPEG);
tag_urational thumbXResolution (tcXResolution, dng_urational (72, 1));
tag_urational thumbYResolution (tcYResolution, dng_urational (72, 1));
tag_uint16 thumbResolutionUnit (tcResolutionUnit, ruInch);
tag_uint32 thumbDataOffset (tcJPEGInterchangeFormat , 0);
tag_uint32 thumbDataLength (tcJPEGInterchangeFormatLength, 0);
if (thumbnail)
{
thumbIFD.Add (&thumbCompression);
thumbIFD.Add (&thumbXResolution);
thumbIFD.Add (&thumbYResolution);
thumbIFD.Add (&thumbResolutionUnit);
thumbIFD.Add (&thumbDataOffset);
thumbIFD.Add (&thumbDataLength);
thumbDataLength.Set (thumbnail->fCompressedData->LogicalSize ());
uint32 thumbOffset = exifOffset + exifSet.Size ();
mainIFD.SetChained (thumbOffset);
thumbDataOffset.Set (thumbOffset + thumbIFD.Size ());
}
// Don't write anything unless the main IFD has some tags.
if (mainIFD.Size () != 0)
{
// Write TIFF Header.
stream.SetWritePosition (0);
stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII);
stream.Put_uint16 (42);
stream.Put_uint32 (8);
// Write the IFDs.
mainIFD.Put (stream);
exifSet.Put (stream);
if (thumbnail)
{
thumbIFD.Put (stream);
stream.Put (thumbnail->fCompressedData->Buffer (),
thumbnail->fCompressedData->LogicalSize ());
}
// Trim the file to this length.
stream.Flush ();
stream.SetLength (stream.Position ());
}
}
return stream.AsMemoryBlock (allocator);
}
/******************************************************************************/
void dng_metadata::SetIPTC (AutoPtr<dng_memory_block> &block, uint64 offset)
{
fIPTCBlock.Reset (block.Release ());
fIPTCOffset = offset;
}
/******************************************************************************/
void dng_metadata::SetIPTC (AutoPtr<dng_memory_block> &block)
{
SetIPTC (block, kDNGStreamInvalidOffset);
}
/******************************************************************************/
void dng_metadata::ClearIPTC ()
{
fIPTCBlock.Reset ();
fIPTCOffset = kDNGStreamInvalidOffset;
}
/*****************************************************************************/
const void * dng_metadata::IPTCData () const
{
if (fIPTCBlock.Get ())
{
return fIPTCBlock->Buffer ();
}
return NULL;
}
/*****************************************************************************/
uint32 dng_metadata::IPTCLength () const
{
if (fIPTCBlock.Get ())
{
return fIPTCBlock->LogicalSize ();
}
return 0;
}
/*****************************************************************************/
uint64 dng_metadata::IPTCOffset () const
{
if (fIPTCBlock.Get ())
{
return fIPTCOffset;
}
return kDNGStreamInvalidOffset;
}
/*****************************************************************************/
dng_fingerprint dng_metadata::IPTCDigest (bool includePadding) const
{
if (IPTCLength ())
{
dng_md5_printer printer;
const uint8 *data = (const uint8 *) IPTCData ();
uint32 count = IPTCLength ();
// Because of some stupid ways of storing the IPTC data, the IPTC
// data might be padded with up to three zeros. The official Adobe
// logic is to include these zeros in the digest. However, older
// versions of the Camera Raw code did not include the padding zeros
// in the digest, so we support both methods and allow either to
// match.
if (!includePadding)
{
uint32 removed = 0;
while ((removed < 3) && (count > 0) && (data [count - 1] == 0))
{
removed++;
count--;
}
}
printer.Process (data, count);
return printer.Result ();
}
return dng_fingerprint ();
}
/******************************************************************************/
#if qDNGUseXMP
void dng_metadata::RebuildIPTC (dng_memory_allocator &allocator,
bool padForTIFF)
{
ClearIPTC ();
fXMP->RebuildIPTC (*this, allocator, padForTIFF);
dng_fingerprint digest = IPTCDigest ();
fXMP->SetIPTCDigest (digest);
}
/*****************************************************************************/
void dng_metadata::ResetXMP (dng_xmp * newXMP)
{
fXMP.Reset (newXMP);
}
/*****************************************************************************/
void dng_metadata::ResetXMPSidecarNewer (dng_xmp * newXMP,
bool inSidecar,
bool isNewer )
{
fXMP.Reset (newXMP);
fXMPinSidecar = inSidecar;
fXMPisNewer = isNewer;
}
/*****************************************************************************/
bool dng_metadata::SetXMP (dng_host &host,
const void *buffer,
uint32 count,
bool xmpInSidecar,
bool xmpIsNewer)
{
bool result = false;
try
{
AutoPtr<dng_xmp> tempXMP (host.Make_dng_xmp ());
tempXMP->Parse (host, buffer, count);
ResetXMPSidecarNewer (tempXMP.Release (), xmpInSidecar, xmpIsNewer);
result = true;
}
catch (dng_exception &except)
{
// Don't ignore transient errors.
if (host.IsTransientError (except.ErrorCode ()))
{
throw;
}
// Eat other parsing errors.
}
catch (...)
{
// Eat unknown parsing exceptions.
}
return result;
}
/*****************************************************************************/
void dng_metadata::SetEmbeddedXMP (dng_host &host,
const void *buffer,
uint32 count)
{
if (SetXMP (host, buffer, count))
{
dng_md5_printer printer;
printer.Process (buffer, count);
fEmbeddedXMPDigest = printer.Result ();
// Remove any sidecar specific tags from embedded XMP.
if (fXMP.Get ())
{
fXMP->Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension");
fXMP->Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest");
}
}
else
{
fEmbeddedXMPDigest.Clear ();
}
}
#endif
/*****************************************************************************/
void dng_metadata::SynchronizeMetadata ()
{
if (!fOriginalExif.Get ())
{
fOriginalExif.Reset (fExif->Clone ());
}
#if qDNGUseXMP
fXMP->ValidateMetadata ();
fXMP->IngestIPTC (*this, fXMPisNewer);
fXMP->SyncExif (*fExif.Get ());
fXMP->SyncOrientation (*this, fXMPinSidecar);
#endif
}
/*****************************************************************************/
void dng_metadata::UpdateDateTime (const dng_date_time_info &dt)
{
fExif->UpdateDateTime (dt);
#if qDNGUseXMP
fXMP->UpdateDateTime (dt);
#endif
}
/*****************************************************************************/
void dng_metadata::UpdateDateTimeToNow ()
{
dng_date_time_info dt;
CurrentDateTimeAndZone (dt);
UpdateDateTime (dt);
#if qDNGUseXMP
fXMP->UpdateMetadataDate (dt);
#endif
}
/*****************************************************************************/
void dng_metadata::UpdateMetadataDateTimeToNow ()
{
dng_date_time_info dt;
CurrentDateTimeAndZone (dt);
#if qDNGUseXMP
fXMP->UpdateMetadataDate (dt);
#endif
}
/*****************************************************************************/
dng_negative::dng_negative (dng_host &host)
: fAllocator (host.Allocator ())
, fModelName ()
, fLocalName ()
, fDefaultCropSizeH ()
, fDefaultCropSizeV ()
, fDefaultCropOriginH (0, 1)
, fDefaultCropOriginV (0, 1)
, fDefaultUserCropT (0, 1)
, fDefaultUserCropL (0, 1)
, fDefaultUserCropB (1, 1)
, fDefaultUserCropR (1, 1)
, fDefaultScaleH (1, 1)
, fDefaultScaleV (1, 1)
, fBestQualityScale (1, 1)
, fOriginalDefaultFinalSize ()
, fOriginalBestQualityFinalSize ()
, fOriginalDefaultCropSizeH ()
, fOriginalDefaultCropSizeV ()
, fRawToFullScaleH (1.0)
, fRawToFullScaleV (1.0)
, fBaselineNoise (100, 100)
, fNoiseReductionApplied (0, 0)
, fNoiseProfile ()
, fBaselineExposure ( 0, 100)
, fBaselineSharpness (100, 100)
, fChromaBlurRadius ()
, fAntiAliasStrength (100, 100)
, fLinearResponseLimit (100, 100)
, fShadowScale (1, 1)
, fColorimetricReference (crSceneReferred)
, fColorChannels (0)
, fAnalogBalance ()
, fCameraNeutral ()
, fCameraWhiteXY ()
, fCameraCalibration1 ()
, fCameraCalibration2 ()
, fCameraCalibrationSignature ()
, fCameraProfile ()
, fAsShotProfileName ()
, fRawImageDigest ()
, fNewRawImageDigest ()
, fRawDataUniqueID ()
, fOriginalRawFileName ()
, fHasOriginalRawFileData (false)
, fOriginalRawFileData ()
, fOriginalRawFileDigest ()
, fDNGPrivateData ()
, fMetadata (host)
, fLinearizationInfo ()
, fMosaicInfo ()
, fOpcodeList1 (1)
, fOpcodeList2 (2)
, fOpcodeList3 (3)
, fStage1Image ()
, fStage2Image ()
, fStage3Image ()
, fStage3Gain (1.0)
, fIsPreview (false)
, fIsDamaged (false)
, fRawImageStage (rawImageStageNone)
, fRawImage ()
, fRawFloatBitDepth (0)
, fRawJPEGImage ()
, fRawJPEGImageDigest ()
, fTransparencyMask ()
, fRawTransparencyMask ()
, fRawTransparencyMaskBitDepth (0)
, fUnflattenedStage3Image ()
{
}
/*****************************************************************************/
dng_negative::~dng_negative ()
{
// Delete any camera profiles owned by this negative.
ClearProfiles ();
}
/******************************************************************************/
void dng_negative::Initialize ()
{
}
/******************************************************************************/
dng_negative * dng_negative::Make (dng_host &host)
{
AutoPtr<dng_negative> result (new dng_negative (host));
if (!result.Get ())
{
ThrowMemoryFull ();
}
result->Initialize ();
return result.Release ();
}
/******************************************************************************/
dng_metadata * dng_negative::CloneInternalMetadata () const
{
return InternalMetadata ().Clone (Allocator ());
}
/******************************************************************************/
dng_orientation dng_negative::ComputeOrientation (const dng_metadata &metadata) const
{
return metadata.BaseOrientation ();
}
/******************************************************************************/
void dng_negative::SetAnalogBalance (const dng_vector &b)
{
real64 minEntry = b.MinEntry ();
if (b.NotEmpty () && minEntry > 0.0)
{
fAnalogBalance = b;
fAnalogBalance.Scale (1.0 / minEntry);
fAnalogBalance.Round (1000000.0);
}
else
{
fAnalogBalance.Clear ();
}
}
/*****************************************************************************/
real64 dng_negative::AnalogBalance (uint32 channel) const
{
DNG_ASSERT (channel < ColorChannels (), "Channel out of bounds");
if (channel < fAnalogBalance.Count ())
{
return fAnalogBalance [channel];
}
return 1.0;
}
/*****************************************************************************/
dng_urational dng_negative::AnalogBalanceR (uint32 channel) const
{
dng_urational result;
result.Set_real64 (AnalogBalance (channel), 1000000);
return result;
}
/******************************************************************************/
void dng_negative::SetCameraNeutral (const dng_vector &n)
{
real64 maxEntry = n.MaxEntry ();
if (n.NotEmpty () && maxEntry > 0.0)
{
fCameraNeutral = n;
fCameraNeutral.Scale (1.0 / maxEntry);
fCameraNeutral.Round (1000000.0);
}
else
{
fCameraNeutral.Clear ();
}
}
/*****************************************************************************/
dng_urational dng_negative::CameraNeutralR (uint32 channel) const
{
dng_urational result;
result.Set_real64 (CameraNeutral () [channel], 1000000);
return result;
}
/******************************************************************************/
void dng_negative::SetCameraWhiteXY (const dng_xy_coord &coord)
{
if (coord.IsValid ())
{
fCameraWhiteXY.x = Round_int32 (coord.x * 1000000.0) / 1000000.0;
fCameraWhiteXY.y = Round_int32 (coord.y * 1000000.0) / 1000000.0;
}
else
{
fCameraWhiteXY.Clear ();
}
}
/*****************************************************************************/
const dng_xy_coord & dng_negative::CameraWhiteXY () const
{
DNG_ASSERT (HasCameraWhiteXY (), "Using undefined CameraWhiteXY");
return fCameraWhiteXY;
}
/*****************************************************************************/
void dng_negative::GetCameraWhiteXY (dng_urational &x,
dng_urational &y) const
{
dng_xy_coord coord = CameraWhiteXY ();
x.Set_real64 (coord.x, 1000000);
y.Set_real64 (coord.y, 1000000);
}
/*****************************************************************************/
void dng_negative::SetCameraCalibration1 (const dng_matrix &m)
{
fCameraCalibration1 = m;
fCameraCalibration1.Round (10000);
}
/******************************************************************************/
void dng_negative::SetCameraCalibration2 (const dng_matrix &m)
{
fCameraCalibration2 = m;
fCameraCalibration2.Round (10000);
}
/******************************************************************************/
void dng_negative::AddProfile (AutoPtr<dng_camera_profile> &profile)
{
// Make sure we have a profile to add.
if (!profile.Get ())
{
return;
}
// We must have some profile name. Use "embedded" if nothing else.
if (profile->Name ().IsEmpty ())
{
profile->SetName (kProfileName_Embedded);
}
// Special case support for reading older DNG files which did not store
// the profile name in the main IFD profile.
if (fCameraProfile.size ())
{
// See the first profile has a default "embedded" name, and has
// the same data as the profile we are adding.
if (fCameraProfile [0]->NameIsEmbedded () &&
fCameraProfile [0]->EqualData (*profile.Get ()))
{
// If the profile we are deleting was read from DNG
// then the new profile should be marked as such also.
if (fCameraProfile [0]->WasReadFromDNG ())
{
profile->SetWasReadFromDNG ();
}
// If the profile we are deleting wasn't read from disk then the new
// profile should be marked as such also.
if (!fCameraProfile [0]->WasReadFromDisk ())
{
profile->SetWasReadFromDisk (false);
}
// Delete the profile with default name.
delete fCameraProfile [0];
fCameraProfile [0] = NULL;
fCameraProfile.erase (fCameraProfile.begin ());
}
}
// Duplicate detection logic. We give a preference to last added profile
// so the profiles end up in a more consistent order no matter what profiles
// happen to be embedded in the DNG.
for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++)
{
// Instead of checking for matching fingerprints, we check that the two
// profiles have the same color and have the same name. This allows two
// profiles that are identical except for copyright string and embed policy
// to be considered duplicates.
const bool equalColorAndSameName = (fCameraProfile [index]->EqualData (*profile.Get ()) &&
fCameraProfile [index]->Name () == profile->Name ());
if (equalColorAndSameName)
{
// If the profile we are deleting was read from DNG
// then the new profile should be marked as such also.
if (fCameraProfile [index]->WasReadFromDNG ())
{
profile->SetWasReadFromDNG ();
}
// If the profile we are deleting wasn't read from disk then the new
// profile should be marked as such also.
if (!fCameraProfile [index]->WasReadFromDisk ())
{
profile->SetWasReadFromDisk (false);
}
// Delete the duplicate profile.
delete fCameraProfile [index];
fCameraProfile [index] = NULL;
fCameraProfile.erase (fCameraProfile.begin () + index);
break;
}
}
// Now add to profile list.
fCameraProfile.push_back (NULL);
fCameraProfile [fCameraProfile.size () - 1] = profile.Release ();
}
/******************************************************************************/
void dng_negative::ClearProfiles ()
{
// Delete any camera profiles owned by this negative.
for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++)
{
if (fCameraProfile [index])
{
delete fCameraProfile [index];
fCameraProfile [index] = NULL;
}
}
// Now empty list.
fCameraProfile.clear ();
}
/*****************************************************************************/
void dng_negative::ClearProfiles (bool clearBuiltinMatrixProfiles,
bool clearReadFromDisk)
{
// If neither flag is set, then there's nothing to do.
if (!clearBuiltinMatrixProfiles &&
!clearReadFromDisk)
{
return;
}
// Delete any camera profiles in this negative that match the specified criteria.
dng_std_vector<dng_camera_profile *>::iterator iter = fCameraProfile.begin ();
dng_std_vector<dng_camera_profile *>::iterator next;
for (; iter != fCameraProfile.end (); iter = next)
{
dng_camera_profile *profile = *iter;
// If the profile is invalid (i.e., NULL pointer), or meets one of the
// specified criteria, then axe it.
if (!profile ||
(clearBuiltinMatrixProfiles && profile->WasBuiltinMatrix ()) ||
(clearReadFromDisk && profile->WasReadFromDisk ()))
{
delete profile;
next = fCameraProfile.erase (iter);
}
// Otherwise, just advance to the next element.
else
{
next = iter + 1;
}
}
}
/******************************************************************************/
uint32 dng_negative::ProfileCount () const
{
return (uint32) fCameraProfile.size ();
}
/******************************************************************************/
const dng_camera_profile & dng_negative::ProfileByIndex (uint32 index) const
{
DNG_ASSERT (index < ProfileCount (),
"Invalid index for ProfileByIndex");
return *fCameraProfile [index];
}
/*****************************************************************************/
const dng_camera_profile * dng_negative::ProfileByID (const dng_camera_profile_id &id,
bool useDefaultIfNoMatch) const
{
uint32 index;
// If this negative does not have any profiles, we are not going to
// find a match.
uint32 profileCount = ProfileCount ();
if (profileCount == 0)
{
return NULL;
}
// If we have both a profile name and fingerprint, try matching both.
if (id.Name ().NotEmpty () && id.Fingerprint ().IsValid ())
{
for (index = 0; index < profileCount; index++)
{
const dng_camera_profile &profile = ProfileByIndex (index);
if (id.Name () == profile.Name () &&
id.Fingerprint () == profile.Fingerprint ())
{
return &profile;
}
}
}
// If we have a name, try matching that.
if (id.Name ().NotEmpty ())
{
for (index = 0; index < profileCount; index++)
{
const dng_camera_profile &profile = ProfileByIndex (index);
if (id.Name () == profile.Name ())
{
return &profile;
}
}
}
// If we have a valid fingerprint, try matching that.
if (id.Fingerprint ().IsValid ())
{
for (index = 0; index < profileCount; index++)
{
const dng_camera_profile &profile = ProfileByIndex (index);
if (id.Fingerprint () == profile.Fingerprint ())
{
return &profile;
}
}
}
// Try "upgrading" profile name versions.
if (id.Name ().NotEmpty ())
{
dng_string baseName;
int32 version;
SplitCameraProfileName (id.Name (),
baseName,
version);
int32 bestIndex = -1;
int32 bestVersion = 0;
for (index = 0; index < profileCount; index++)
{
const dng_camera_profile &profile = ProfileByIndex (index);
if (profile.Name ().StartsWith (baseName.Get ()))
{
dng_string testBaseName;
int32 testVersion;
SplitCameraProfileName (profile.Name (),
testBaseName,
testVersion);
if (bestIndex == -1 || testVersion > bestVersion)
{
bestIndex = index;
bestVersion = testVersion;
}
}
}
if (bestIndex != -1)
{
return &ProfileByIndex (bestIndex);
}
}
// Did not find a match any way. See if we should return a default value.
if (useDefaultIfNoMatch)
{
return &ProfileByIndex (0);
}
// Found nothing.
return NULL;
}
/*****************************************************************************/
const dng_camera_profile * dng_negative::ComputeCameraProfileToEmbed
(const dng_metadata & /* metadata */) const
{
uint32 index;
uint32 count = ProfileCount ();
if (count == 0)
{
return NULL;
}
// First try to look for the first profile that was already in the DNG
// when we read it.
for (index = 0; index < count; index++)
{
const dng_camera_profile &profile (ProfileByIndex (index));
if (profile.WasReadFromDNG ())
{
return &profile;
}
}
// Next we look for the first profile that is legal to embed.
for (index = 0; index < count; index++)
{
const dng_camera_profile &profile (ProfileByIndex (index));
if (profile.IsLegalToEmbed ())
{
return &profile;
}
}
// Else just return the first profile.
return fCameraProfile [0];
}
/*****************************************************************************/
dng_color_spec * dng_negative::MakeColorSpec (const dng_camera_profile_id &id) const
{
dng_color_spec *spec = new dng_color_spec (*this, ProfileByID (id));
if (!spec)
{
ThrowMemoryFull ();
}
return spec;
}
/*****************************************************************************/
dng_fingerprint dng_negative::FindImageDigest (dng_host &host,
const dng_image &image) const
{
dng_md5_printer printer;
dng_pixel_buffer buffer (image.Bounds (), 0, image.Planes (),
image.PixelType (), pcInterleaved, NULL);
// Sometimes we expand 8-bit data to 16-bit data while reading or
// writing, so always compute the digest of 8-bit data as 16-bits.
if (buffer.fPixelType == ttByte)
{
buffer.fPixelType = ttShort;
buffer.fPixelSize = 2;
}
const uint32 kBufferRows = 16;
uint32 bufferBytes = 0;
if (!SafeUint32Mult (kBufferRows, buffer.fRowStep, &bufferBytes) ||
!SafeUint32Mult (bufferBytes, buffer.fPixelSize, &bufferBytes))
{
ThrowMemoryFull("Arithmetic overflow computing buffer size.");
}
AutoPtr<dng_memory_block> bufferData (host.Allocate (bufferBytes));
buffer.fData = bufferData->Buffer ();
dng_rect area;
dng_tile_iterator iter (dng_point (kBufferRows,
image.Width ()),
image.Bounds ());
while (iter.GetOneTile (area))
{
host.SniffForAbort ();
buffer.fArea = area;
image.Get (buffer);
uint32 count = buffer.fArea.H () *
buffer.fRowStep *
buffer.fPixelSize;
#if qDNGBigEndian
// We need to use the same byte order to compute
// the digest, no matter the native order. Little-endian
// is more common now, so use that.
switch (buffer.fPixelSize)
{
case 1:
break;
case 2:
{
DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1);
break;
}
case 4:
{
DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2);
break;
}
default:
{
DNG_REPORT ("Unexpected pixel size");
break;
}
}
#endif
printer.Process (buffer.fData,
count);
}
return printer.Result ();
}
/*****************************************************************************/
void dng_negative::FindRawImageDigest (dng_host &host) const
{
if (fRawImageDigest.IsNull ())
{
// Since we are adding the floating point and transparency support
// in DNG 1.4, and there are no legacy floating point or transparent
// DNGs, switch to using the more MP friendly algorithm to compute
// the digest for these images.
if (RawImage ().PixelType () == ttFloat || RawTransparencyMask ())
{
FindNewRawImageDigest (host);
fRawImageDigest = fNewRawImageDigest;
}
else
{
#if qDNGValidate
dng_timer timeScope ("FindRawImageDigest time");
#endif
fRawImageDigest = FindImageDigest (host, RawImage ());
}
}
}
/*****************************************************************************/
class dng_find_new_raw_image_digest_task : public dng_area_task
{
private:
enum
{
kTileSize = 256
};
const dng_image &fImage;
uint32 fPixelType;
uint32 fPixelSize;
uint32 fTilesAcross;
uint32 fTilesDown;
uint32 fTileCount;
AutoArray<dng_fingerprint> fTileHash;
AutoPtr<dng_memory_block> fBufferData [kMaxMPThreads];
public:
dng_find_new_raw_image_digest_task (const dng_image &image,
uint32 pixelType)
: fImage (image)
, fPixelType (pixelType)
, fPixelSize (TagTypeSize (pixelType))
, fTilesAcross (0)
, fTilesDown (0)
, fTileCount (0)
, fTileHash ()
{
fMinTaskArea = 1;
fUnitCell = dng_point (Min_int32 (kTileSize, fImage.Bounds ().H ()),
Min_int32 (kTileSize, fImage.Bounds ().W ()));
fMaxTileSize = fUnitCell;
}
virtual void Start (uint32 threadCount,
const dng_point &tileSize,
dng_memory_allocator *allocator,
dng_abort_sniffer * /* sniffer */)
{
if (tileSize != fUnitCell)
{
ThrowProgramError ();
}
fTilesAcross = (fImage.Bounds ().W () + fUnitCell.h - 1) / fUnitCell.h;
fTilesDown = (fImage.Bounds ().H () + fUnitCell.v - 1) / fUnitCell.v;
fTileCount = fTilesAcross * fTilesDown;
fTileHash.Reset (fTileCount);
const uint32 bufferSize =
ComputeBufferSize(fPixelType, tileSize, fImage.Planes(),
padNone);
for (uint32 index = 0; index < threadCount; index++)
{
fBufferData [index].Reset (allocator->Allocate (bufferSize));
}
}
virtual void Process (uint32 threadIndex,
const dng_rect &tile,
dng_abort_sniffer * /* sniffer */)
{
int32 colIndex = (tile.l - fImage.Bounds ().l) / fUnitCell.h;
int32 rowIndex = (tile.t - fImage.Bounds ().t) / fUnitCell.v;
DNG_ASSERT (tile.l == fImage.Bounds ().l + colIndex * fUnitCell.h &&
tile.t == fImage.Bounds ().t + rowIndex * fUnitCell.v,
"Bad tile origin");
uint32 tileIndex = rowIndex * fTilesAcross + colIndex;
dng_pixel_buffer buffer (tile, 0, fImage.Planes (),
fPixelType, pcPlanar,
fBufferData [threadIndex]->Buffer ());
fImage.Get (buffer);
uint32 count = buffer.fPlaneStep *
buffer.fPlanes *
buffer.fPixelSize;
#if qDNGBigEndian
// We need to use the same byte order to compute
// the digest, no matter the native order. Little-endian
// is more common now, so use that.
switch (buffer.fPixelSize)
{
case 1:
break;
case 2:
{
DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1);
break;
}
case 4:
{
DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2);
break;
}
default:
{
DNG_REPORT ("Unexpected pixel size");
break;
}
}
#endif
dng_md5_printer printer;
printer.Process (buffer.fData, count);
fTileHash [tileIndex] = printer.Result ();
}
dng_fingerprint Result ()
{
dng_md5_printer printer;
for (uint32 tileIndex = 0; tileIndex < fTileCount; tileIndex++)
{
printer.Process (fTileHash [tileIndex] . data, 16);
}
return printer.Result ();
}
};
/*****************************************************************************/
void dng_negative::FindNewRawImageDigest (dng_host &host) const
{
if (fNewRawImageDigest.IsNull ())
{
#if qDNGValidate
dng_timer timeScope ("FindNewRawImageDigest time");
#endif
// Find fast digest of the raw image.
{
const dng_image &rawImage = RawImage ();
// Find pixel type that will be saved in the file. When saving DNGs, we convert
// some 16-bit data to 8-bit data, so we need to do the matching logic here.
uint32 rawPixelType = rawImage.PixelType ();
if (rawPixelType == ttShort)
{
// See if we are using a linearization table with <= 256 entries, in which
// case the useful data will all fit within 8-bits.
const dng_linearization_info *rangeInfo = GetLinearizationInfo ();
if (rangeInfo)
{
if (rangeInfo->fLinearizationTable.Get ())
{
uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1;
if (entries <= 256)
{
rawPixelType = ttByte;
}
}
}
}
// Find the fast digest on the raw image.
dng_find_new_raw_image_digest_task task (rawImage, rawPixelType);
host.PerformAreaTask (task, rawImage.Bounds ());
fNewRawImageDigest = task.Result ();
}
// If there is a transparancy mask, we need to include that in the
// digest also.
if (RawTransparencyMask () != NULL)
{
// Find the fast digest on the raw mask.
dng_fingerprint maskDigest;
{
dng_find_new_raw_image_digest_task task (*RawTransparencyMask (),
RawTransparencyMask ()->PixelType ());
host.PerformAreaTask (task, RawTransparencyMask ()->Bounds ());
maskDigest = task.Result ();
}
// Combine the two digests into a single digest.
dng_md5_printer printer;
printer.Process (fNewRawImageDigest.data, 16);
printer.Process (maskDigest.data, 16);
fNewRawImageDigest = printer.Result ();
}
}
}
/*****************************************************************************/
void dng_negative::ValidateRawImageDigest (dng_host &host)
{
if (Stage1Image () && !IsPreview () && (fRawImageDigest .IsValid () ||
fNewRawImageDigest.IsValid ()))
{
bool isNewDigest = fNewRawImageDigest.IsValid ();
dng_fingerprint &rawDigest = isNewDigest ? fNewRawImageDigest
: fRawImageDigest;
// For lossy compressed JPEG images, we need to compare the stored
// digest to the digest computed from the compressed data, since
// decompressing lossy JPEG data is itself a lossy process.
if (RawJPEGImageDigest ().IsValid () || RawJPEGImage ())
{
// Compute the raw JPEG image digest if we have not done so
// already.
FindRawJPEGImageDigest (host);
if (rawDigest != RawJPEGImageDigest ())
{
#if qDNGValidate
ReportError ("RawImageDigest does not match raw jpeg image");
#else
SetIsDamaged (true);
#endif
}
}
// Else we can compare the stored digest to the image in memory.
else
{
dng_fingerprint oldDigest = rawDigest;
try
{
rawDigest.Clear ();
if (isNewDigest)
{
FindNewRawImageDigest (host);
}
else
{
FindRawImageDigest (host);
}
}
catch (...)
{
rawDigest = oldDigest;
throw;
}
if (oldDigest != rawDigest)
{
#if qDNGValidate
if (isNewDigest)
{
ReportError ("NewRawImageDigest does not match raw image");
}
else
{
ReportError ("RawImageDigest does not match raw image");
}
SetIsDamaged (true);
#else
if (!isNewDigest)
{
// Note that Lightroom 1.4 Windows had a bug that corrupts the
// first four bytes of the RawImageDigest tag. So if the last
// twelve bytes match, this is very likely the result of the
// bug, and not an actual corrupt file. So don't report this
// to the user--just fix it.
{
bool matchLast12 = true;
for (uint32 j = 4; j < 16; j++)
{
matchLast12 = matchLast12 && (oldDigest.data [j] == fRawImageDigest.data [j]);
}
if (matchLast12)
{
return;
}
}
// Sometimes Lightroom 1.4 would corrupt more than the first four
// bytes, but for all those files that I have seen so far the
// resulting first four bytes are 0x08 0x00 0x00 0x00.
if (oldDigest.data [0] == 0x08 &&
oldDigest.data [1] == 0x00 &&
oldDigest.data [2] == 0x00 &&
oldDigest.data [3] == 0x00)
{
return;
}
}
SetIsDamaged (true);
#endif
}
}
}
}
/*****************************************************************************/
// If the raw data unique ID is missing, compute one based on a MD5 hash of
// the raw image hash and the model name, plus other commonly changed
// data that can affect rendering.
void dng_negative::FindRawDataUniqueID (dng_host &host) const
{
if (fRawDataUniqueID.IsNull ())
{
dng_md5_printer_stream printer;
// If we have a raw jpeg image, it is much faster to
// use its digest as part of the unique ID since
// the data size is much smaller. We cannot use it
// if there a transparency mask, since that is not
// included in the RawJPEGImageDigest.
if (RawJPEGImage () && !RawTransparencyMask ())
{
FindRawJPEGImageDigest (host);
printer.Put (fRawJPEGImageDigest.data, 16);
}
// Include the new raw image digest in the unique ID.
else
{
FindNewRawImageDigest (host);
printer.Put (fNewRawImageDigest.data, 16);
}
// Include model name.
printer.Put (ModelName ().Get (),
ModelName ().Length ());
// Include default crop area, since DNG Recover Edges can modify
// these values and they affect rendering.
printer.Put_uint32 (fDefaultCropSizeH.n);
printer.Put_uint32 (fDefaultCropSizeH.d);
printer.Put_uint32 (fDefaultCropSizeV.n);
printer.Put_uint32 (fDefaultCropSizeV.d);
printer.Put_uint32 (fDefaultCropOriginH.n);
printer.Put_uint32 (fDefaultCropOriginH.d);
printer.Put_uint32 (fDefaultCropOriginV.n);
printer.Put_uint32 (fDefaultCropOriginV.d);
// Include default user crop.
printer.Put_uint32 (fDefaultUserCropT.n);
printer.Put_uint32 (fDefaultUserCropT.d);
printer.Put_uint32 (fDefaultUserCropL.n);
printer.Put_uint32 (fDefaultUserCropL.d);
printer.Put_uint32 (fDefaultUserCropB.n);
printer.Put_uint32 (fDefaultUserCropB.d);
printer.Put_uint32 (fDefaultUserCropR.n);
printer.Put_uint32 (fDefaultUserCropR.d);
// Include opcode lists, since lens correction utilities can modify
// these values and they affect rendering.
fOpcodeList1.FingerprintToStream (printer);
fOpcodeList2.FingerprintToStream (printer);
fOpcodeList3.FingerprintToStream (printer);
fRawDataUniqueID = printer.Result ();
}
}
/******************************************************************************/
// Forces recomputation of RawDataUniqueID, useful to call
// after modifying the opcode lists, etc.
void dng_negative::RecomputeRawDataUniqueID (dng_host &host)
{
fRawDataUniqueID.Clear ();
FindRawDataUniqueID (host);
}
/******************************************************************************/
void dng_negative::FindOriginalRawFileDigest () const
{
if (fOriginalRawFileDigest.IsNull () && fOriginalRawFileData.Get ())
{
dng_md5_printer printer;
printer.Process (fOriginalRawFileData->Buffer (),
fOriginalRawFileData->LogicalSize ());
fOriginalRawFileDigest = printer.Result ();
}
}
/*****************************************************************************/
void dng_negative::ValidateOriginalRawFileDigest ()
{
if (fOriginalRawFileDigest.IsValid () && fOriginalRawFileData.Get ())
{
dng_fingerprint oldDigest = fOriginalRawFileDigest;
try
{
fOriginalRawFileDigest.Clear ();
FindOriginalRawFileDigest ();
}
catch (...)
{
fOriginalRawFileDigest = oldDigest;
throw;
}
if (oldDigest != fOriginalRawFileDigest)
{
#if qDNGValidate
ReportError ("OriginalRawFileDigest does not match OriginalRawFileData");
#else
SetIsDamaged (true);
#endif
// Don't "repair" the original image data digest. Once it is
// bad, it stays bad. The user cannot tell by looking at the image
// whether the damage is acceptable and can be ignored in the
// future.
fOriginalRawFileDigest = oldDigest;
}
}
}
/******************************************************************************/
dng_rect dng_negative::DefaultCropArea () const
{
// First compute the area using simple rounding.
dng_rect result;
result.l = Round_int32 (fDefaultCropOriginH.As_real64 () * fRawToFullScaleH);
result.t = Round_int32 (fDefaultCropOriginV.As_real64 () * fRawToFullScaleV);
result.r = result.l + Round_int32 (fDefaultCropSizeH.As_real64 () * fRawToFullScaleH);
result.b = result.t + Round_int32 (fDefaultCropSizeV.As_real64 () * fRawToFullScaleV);
// Sometimes the simple rounding causes the resulting default crop
// area to slide off the scaled image area. So we force this not
// to happen. We only do this if the image is not stubbed.
const dng_image *image = Stage3Image ();
if (image)
{
dng_point imageSize = image->Size ();
if (result.r > imageSize.h)
{
result.l -= result.r - imageSize.h;
result.r = imageSize.h;
}
if (result.b > imageSize.v)
{
result.t -= result.b - imageSize.v;
result.b = imageSize.v;
}
}
return result;
}
/*****************************************************************************/
real64 dng_negative::TotalBaselineExposure (const dng_camera_profile_id &profileID) const
{
real64 total = BaselineExposure ();
const dng_camera_profile *profile = ProfileByID (profileID);
if (profile)
{
real64 offset = profile->BaselineExposureOffset ().As_real64 ();
total += offset;
}
return total;
}
/******************************************************************************/
void dng_negative::SetShadowScale (const dng_urational &scale)
{
if (scale.d > 0)
{
real64 s = scale.As_real64 ();
if (s > 0.0 && s <= 1.0)
{
fShadowScale = scale;
}
}
}
/******************************************************************************/
void dng_negative::SetActiveArea (const dng_rect &area)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fActiveArea = area;
}
/******************************************************************************/
void dng_negative::SetMaskedAreas (uint32 count,
const dng_rect *area)
{
DNG_ASSERT (count <= kMaxMaskedAreas, "Too many masked areas");
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fMaskedAreaCount = Min_uint32 (count, kMaxMaskedAreas);
for (uint32 index = 0; index < info.fMaskedAreaCount; index++)
{
info.fMaskedArea [index] = area [index];
}
}
/*****************************************************************************/
void dng_negative::SetLinearization (AutoPtr<dng_memory_block> &curve)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fLinearizationTable.Reset (curve.Release ());
}
/*****************************************************************************/
void dng_negative::SetBlackLevel (real64 black,
int32 plane)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fBlackLevelRepeatRows = 1;
info.fBlackLevelRepeatCols = 1;
if (plane < 0)
{
for (uint32 j = 0; j < kMaxSamplesPerPixel; j++)
{
info.fBlackLevel [0] [0] [j] = black;
}
}
else
{
info.fBlackLevel [0] [0] [plane] = black;
}
info.RoundBlacks ();
}
/*****************************************************************************/
void dng_negative::SetQuadBlacks (real64 black0,
real64 black1,
real64 black2,
real64 black3,
int32 plane)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fBlackLevelRepeatRows = 2;
info.fBlackLevelRepeatCols = 2;
if (plane < 0)
{
for (uint32 j = 0; j < kMaxSamplesPerPixel; j++)
{
info.fBlackLevel [0] [0] [j] = black0;
info.fBlackLevel [0] [1] [j] = black1;
info.fBlackLevel [1] [0] [j] = black2;
info.fBlackLevel [1] [1] [j] = black3;
}
}
else
{
info.fBlackLevel [0] [0] [plane] = black0;
info.fBlackLevel [0] [1] [plane] = black1;
info.fBlackLevel [1] [0] [plane] = black2;
info.fBlackLevel [1] [1] [plane] = black3;
}
info.RoundBlacks ();
}
/*****************************************************************************/
void dng_negative::SetRowBlacks (const real64 *blacks,
uint32 count)
{
if (count)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
uint32 byteCount = 0;
if (!SafeUint32Mult (count, (uint32) sizeof (real64), &byteCount))
{
ThrowMemoryFull("Arithmetic overflow computing byte count.");
}
info.fBlackDeltaV.Reset (Allocator ().Allocate (byteCount));
DoCopyBytes (blacks,
info.fBlackDeltaV->Buffer (),
byteCount);
info.RoundBlacks ();
}
else if (fLinearizationInfo.Get ())
{
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fBlackDeltaV.Reset ();
}
}
/*****************************************************************************/
void dng_negative::SetColumnBlacks (const real64 *blacks,
uint32 count)
{
if (count)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
uint32 byteCount = 0;
if (!SafeUint32Mult (count, (uint32) sizeof (real64), &byteCount))
{
ThrowMemoryFull("Arithmetic overflow computing byte count.");
}
info.fBlackDeltaH.Reset (Allocator ().Allocate (byteCount));
DoCopyBytes (blacks,
info.fBlackDeltaH->Buffer (),
byteCount);
info.RoundBlacks ();
}
else if (fLinearizationInfo.Get ())
{
dng_linearization_info &info = *fLinearizationInfo.Get ();
info.fBlackDeltaH.Reset ();
}
}
/*****************************************************************************/
uint32 dng_negative::WhiteLevel (uint32 plane) const
{
if (fLinearizationInfo.Get ())
{
const dng_linearization_info &info = *fLinearizationInfo.Get ();
return Round_uint32 (info.fWhiteLevel [plane]);
}
if (RawImage ().PixelType () == ttFloat)
{
return 1;
}
return 0x0FFFF;
}
/*****************************************************************************/
void dng_negative::SetWhiteLevel (uint32 white,
int32 plane)
{
NeedLinearizationInfo ();
dng_linearization_info &info = *fLinearizationInfo.Get ();
if (plane < 0)
{
for (uint32 j = 0; j < kMaxSamplesPerPixel; j++)
{
info.fWhiteLevel [j] = (real64) white;
}
}
else
{
info.fWhiteLevel [plane] = (real64) white;
}
}
/******************************************************************************/
void dng_negative::SetColorKeys (ColorKeyCode color0,
ColorKeyCode color1,
ColorKeyCode color2,
ColorKeyCode color3)
{
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
info.fCFAPlaneColor [0] = color0;
info.fCFAPlaneColor [1] = color1;
info.fCFAPlaneColor [2] = color2;
info.fCFAPlaneColor [3] = color3;
}
/******************************************************************************/
void dng_negative::SetBayerMosaic (uint32 phase)
{
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0];
ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1];
ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2];
info.fCFAPatternSize = dng_point (2, 2);
switch (phase)
{
case 0:
{
info.fCFAPattern [0] [0] = color1;
info.fCFAPattern [0] [1] = color0;
info.fCFAPattern [1] [0] = color2;
info.fCFAPattern [1] [1] = color1;
break;
}
case 1:
{
info.fCFAPattern [0] [0] = color0;
info.fCFAPattern [0] [1] = color1;
info.fCFAPattern [1] [0] = color1;
info.fCFAPattern [1] [1] = color2;
break;
}
case 2:
{
info.fCFAPattern [0] [0] = color2;
info.fCFAPattern [0] [1] = color1;
info.fCFAPattern [1] [0] = color1;
info.fCFAPattern [1] [1] = color0;
break;
}
case 3:
{
info.fCFAPattern [0] [0] = color1;
info.fCFAPattern [0] [1] = color2;
info.fCFAPattern [1] [0] = color0;
info.fCFAPattern [1] [1] = color1;
break;
}
}
info.fColorPlanes = 3;
info.fCFALayout = 1;
}
/******************************************************************************/
void dng_negative::SetFujiMosaic (uint32 phase)
{
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0];
ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1];
ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2];
info.fCFAPatternSize = dng_point (2, 4);
switch (phase)
{
case 0:
{
info.fCFAPattern [0] [0] = color0;
info.fCFAPattern [0] [1] = color1;
info.fCFAPattern [0] [2] = color2;
info.fCFAPattern [0] [3] = color1;
info.fCFAPattern [1] [0] = color2;
info.fCFAPattern [1] [1] = color1;
info.fCFAPattern [1] [2] = color0;
info.fCFAPattern [1] [3] = color1;
break;
}
case 1:
{
info.fCFAPattern [0] [0] = color2;
info.fCFAPattern [0] [1] = color1;
info.fCFAPattern [0] [2] = color0;
info.fCFAPattern [0] [3] = color1;
info.fCFAPattern [1] [0] = color0;
info.fCFAPattern [1] [1] = color1;
info.fCFAPattern [1] [2] = color2;
info.fCFAPattern [1] [3] = color1;
break;
}
}
info.fColorPlanes = 3;
info.fCFALayout = 2;
}
/*****************************************************************************/
void dng_negative::SetFujiMosaic6x6 (uint32 phase)
{
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0];
ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1];
ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2];
const uint32 patSize = 6;
info.fCFAPatternSize = dng_point (patSize, patSize);
info.fCFAPattern [0] [0] = color1;
info.fCFAPattern [0] [1] = color2;
info.fCFAPattern [0] [2] = color1;
info.fCFAPattern [0] [3] = color1;
info.fCFAPattern [0] [4] = color0;
info.fCFAPattern [0] [5] = color1;
info.fCFAPattern [1] [0] = color0;
info.fCFAPattern [1] [1] = color1;
info.fCFAPattern [1] [2] = color0;
info.fCFAPattern [1] [3] = color2;
info.fCFAPattern [1] [4] = color1;
info.fCFAPattern [1] [5] = color2;
info.fCFAPattern [2] [0] = color1;
info.fCFAPattern [2] [1] = color2;
info.fCFAPattern [2] [2] = color1;
info.fCFAPattern [2] [3] = color1;
info.fCFAPattern [2] [4] = color0;
info.fCFAPattern [2] [5] = color1;
info.fCFAPattern [3] [0] = color1;
info.fCFAPattern [3] [1] = color0;
info.fCFAPattern [3] [2] = color1;
info.fCFAPattern [3] [3] = color1;
info.fCFAPattern [3] [4] = color2;
info.fCFAPattern [3] [5] = color1;
info.fCFAPattern [4] [0] = color2;
info.fCFAPattern [4] [1] = color1;
info.fCFAPattern [4] [2] = color2;
info.fCFAPattern [4] [3] = color0;
info.fCFAPattern [4] [4] = color1;
info.fCFAPattern [4] [5] = color0;
info.fCFAPattern [5] [0] = color1;
info.fCFAPattern [5] [1] = color0;
info.fCFAPattern [5] [2] = color1;
info.fCFAPattern [5] [3] = color1;
info.fCFAPattern [5] [4] = color2;
info.fCFAPattern [5] [5] = color1;
DNG_REQUIRE (phase >= 0 && phase < patSize * patSize,
"Bad phase in SetFujiMosaic6x6.");
if (phase > 0)
{
dng_mosaic_info temp = info;
uint32 phaseRow = phase / patSize;
uint32 phaseCol = phase - (phaseRow * patSize);
for (uint32 dstRow = 0; dstRow < patSize; dstRow++)
{
uint32 srcRow = (dstRow + phaseRow) % patSize;
for (uint32 dstCol = 0; dstCol < patSize; dstCol++)
{
uint32 srcCol = (dstCol + phaseCol) % patSize;
temp.fCFAPattern [dstRow] [dstCol] = info.fCFAPattern [srcRow] [srcCol];
}
}
info = temp;
}
info.fColorPlanes = 3;
info.fCFALayout = 1;
}
/******************************************************************************/
void dng_negative::SetQuadMosaic (uint32 pattern)
{
// The pattern of the four colors is assumed to be repeat at least every two
// columns and eight rows. The pattern is encoded as a 32-bit integer,
// with every two bits encoding a color, in scan order for two columns and
// eight rows (lsb is first). The usual color coding is:
//
// 0 = Green
// 1 = Magenta
// 2 = Cyan
// 3 = Yellow
//
// Examples:
//
// PowerShot 600 uses 0xe1e4e1e4:
//
// 0 1 2 3 4 5
// 0 G M G M G M
// 1 C Y C Y C Y
// 2 M G M G M G
// 3 C Y C Y C Y
//
// PowerShot A5 uses 0x1e4e1e4e:
//
// 0 1 2 3 4 5
// 0 C Y C Y C Y
// 1 G M G M G M
// 2 C Y C Y C Y
// 3 M G M G M G
//
// PowerShot A50 uses 0x1b4e4b1e:
//
// 0 1 2 3 4 5
// 0 C Y C Y C Y
// 1 M G M G M G
// 2 Y C Y C Y C
// 3 G M G M G M
// 4 C Y C Y C Y
// 5 G M G M G M
// 6 Y C Y C Y C
// 7 M G M G M G
//
// PowerShot Pro70 uses 0x1e4b4e1b:
//
// 0 1 2 3 4 5
// 0 Y C Y C Y C
// 1 M G M G M G
// 2 C Y C Y C Y
// 3 G M G M G M
// 4 Y C Y C Y C
// 5 G M G M G M
// 6 C Y C Y C Y
// 7 M G M G M G
//
// PowerShots Pro90 and G1 use 0xb4b4b4b4:
//
// 0 1 2 3 4 5
// 0 G M G M G M
// 1 Y C Y C Y C
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
if (((pattern >> 16) & 0x0FFFF) != (pattern & 0x0FFFF))
{
info.fCFAPatternSize = dng_point (8, 2);
}
else if (((pattern >> 8) & 0x0FF) != (pattern & 0x0FF))
{
info.fCFAPatternSize = dng_point (4, 2);
}
else
{
info.fCFAPatternSize = dng_point (2, 2);
}
for (int32 row = 0; row < info.fCFAPatternSize.v; row++)
{
for (int32 col = 0; col < info.fCFAPatternSize.h; col++)
{
uint32 index = (pattern >> ((((row << 1) & 14) + (col & 1)) << 1)) & 3;
info.fCFAPattern [row] [col] = info.fCFAPlaneColor [index];
}
}
info.fColorPlanes = 4;
info.fCFALayout = 1;
}
/******************************************************************************/
void dng_negative::SetGreenSplit (uint32 split)
{
NeedMosaicInfo ();
dng_mosaic_info &info = *fMosaicInfo.Get ();
info.fBayerGreenSplit = split;
}
/*****************************************************************************/
void dng_negative::Parse (dng_host &host,
dng_stream &stream,
dng_info &info)
{
// Shared info.
dng_shared &shared = *(info.fShared.Get ());
// Find IFD holding the main raw information.
dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get ();
// Model name.
SetModelName (shared.fUniqueCameraModel.Get ());
// Localized model name.
SetLocalName (shared.fLocalizedCameraModel.Get ());
// Base orientation.
{
uint32 orientation = info.fIFD [0]->fOrientation;
if (orientation >= 1 && orientation <= 8)
{
SetBaseOrientation (dng_orientation::TIFFtoDNG (orientation));
}
}
// Default crop rectangle.
SetDefaultCropSize (rawIFD.fDefaultCropSizeH,
rawIFD.fDefaultCropSizeV);
SetDefaultCropOrigin (rawIFD.fDefaultCropOriginH,
rawIFD.fDefaultCropOriginV);
// Default user crop rectangle.
SetDefaultUserCrop (rawIFD.fDefaultUserCropT,
rawIFD.fDefaultUserCropL,
rawIFD.fDefaultUserCropB,
rawIFD.fDefaultUserCropR);
// Default scale.
SetDefaultScale (rawIFD.fDefaultScaleH,
rawIFD.fDefaultScaleV);
// Best quality scale.
SetBestQualityScale (rawIFD.fBestQualityScale);
// Baseline noise.
SetBaselineNoise (shared.fBaselineNoise.As_real64 ());
// NoiseReductionApplied.
SetNoiseReductionApplied (shared.fNoiseReductionApplied);
// NoiseProfile.
SetNoiseProfile (shared.fNoiseProfile);
// Baseline exposure.
SetBaselineExposure (shared.fBaselineExposure.As_real64 ());
// Baseline sharpness.
SetBaselineSharpness (shared.fBaselineSharpness.As_real64 ());
// Chroma blur radius.
SetChromaBlurRadius (rawIFD.fChromaBlurRadius);
// Anti-alias filter strength.
SetAntiAliasStrength (rawIFD.fAntiAliasStrength);
// Linear response limit.
SetLinearResponseLimit (shared.fLinearResponseLimit.As_real64 ());
// Shadow scale.
SetShadowScale (shared.fShadowScale);
// Colorimetric reference.
SetColorimetricReference (shared.fColorimetricReference);
// Color channels.
SetColorChannels (shared.fCameraProfile.fColorPlanes);
// Analog balance.
if (shared.fAnalogBalance.NotEmpty ())
{
SetAnalogBalance (shared.fAnalogBalance);
}
// Camera calibration matrices
if (shared.fCameraCalibration1.NotEmpty ())
{
SetCameraCalibration1 (shared.fCameraCalibration1);
}
if (shared.fCameraCalibration2.NotEmpty ())
{
SetCameraCalibration2 (shared.fCameraCalibration2);
}
if (shared.fCameraCalibration1.NotEmpty () ||
shared.fCameraCalibration2.NotEmpty ())
{
SetCameraCalibrationSignature (shared.fCameraCalibrationSignature.Get ());
}
// Embedded camera profiles.
if (shared.fCameraProfile.fColorPlanes > 1)
{
if (qDNGValidate || host.NeedsMeta () || host.NeedsImage ())
{
// Add profile from main IFD.
{
AutoPtr<dng_camera_profile> profile (new dng_camera_profile ());
dng_camera_profile_info &profileInfo = shared.fCameraProfile;
profile->Parse (stream, profileInfo);
// The main embedded profile must be valid.
if (!profile->IsValid (shared.fCameraProfile.fColorPlanes))
{