/*****************************************************************************/
// 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_validate.cpp#2 $ */
/* $DateTime: 2012/06/14 20:24:41 $ */
/* $Change: 835078 $ */
/* $Author: tknoll $ */
// Process exit codes
// ------------------
//
// As usual, 0 indicates success.
//
// If an exception occurs, the exit code will be equal to:
//
// DNG SDK error code - 100000 + 100
//
// For example, the error dng_error_memory, which has a DNG SDK error code of
// 100005, is returned as an exit code of 105.
//
// This convention accounts for the fact that the shell truncates process exit
// codes to 8 bits and that the exit code 1 is used by ASAN to signal that a
// memory error occurred (so mapping the first DNG SDK error code to an exit
// code of 1 would not be a good idea).
/*****************************************************************************/
#include "dng_color_space.h"
#include "dng_date_time.h"
#include "dng_exceptions.h"
#include "dng_file_stream.h"
#include "dng_globals.h"
#include "dng_host.h"
#include "dng_ifd.h"
#include "dng_image_writer.h"
#include "dng_info.h"
#include "dng_linearization_info.h"
#include "dng_mosaic_info.h"
#include "dng_negative.h"
#include "dng_preview.h"
#include "dng_render.h"
#include "dng_simple_image.h"
#include "dng_tag_codes.h"
#include "dng_tag_types.h"
#include "dng_tag_values.h"
#if qDNGUseXMP
#include "dng_xmp.h"
#include "dng_xmp_sdk.h"
#endif
/*****************************************************************************/
#if qDNGValidateTarget
/*****************************************************************************/
#define kDNGValidateVersion "1.4"
/*****************************************************************************/
static bool gFourColorBayer = false;
static int32 gMosaicPlane = -1;
static uint32 gPreferredSize = 0;
static uint32 gMinimumSize = 0;
static uint32 gMaximumSize = 0;
static uint32 gProxyDNGSize = 0;
static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get ();
static uint32 gFinalPixelType = ttByte;
static dng_string gDumpStage1;
static dng_string gDumpStage2;
static dng_string gDumpStage3;
static dng_string gDumpTIF;
static dng_string gDumpDNG;
/*****************************************************************************/
static dng_error_code dng_validate (const char *filename)
{
printf ("Validating \"%s\"...\n", filename);
try
{
dng_file_stream stream (filename);
dng_host host;
host.SetPreferredSize (gPreferredSize);
host.SetMinimumSize (gMinimumSize );
host.SetMaximumSize (gMaximumSize );
host.ValidateSizes ();
if (host.MinimumSize ())
{
host.SetForPreview (true);
gDumpDNG.Clear ();
}
if (gDumpDNG.NotEmpty ())
{
host.SetSaveDNGVersion (dngVersion_SaveDefault);
host.SetSaveLinearDNG (false);
host.SetKeepOriginalFile (false);
}
// Read into the negative.
AutoPtr<dng_negative> negative;
{
dng_info info;
info.Parse (host, stream);
info.PostParse (host);
if (!info.IsValidDNG ())
{
return dng_error_bad_format;
}
negative.Reset (host.Make_dng_negative ());
negative->Parse (host, stream, info);
negative->PostParse (host, stream, info);
{
dng_timer timer ("Raw image read time");
negative->ReadStage1Image (host, stream, info);
}
if (info.fMaskIndex != -1)
{
dng_timer timer ("Transparency mask read time");
negative->ReadTransparencyMask (host, stream, info);
}
negative->ValidateRawImageDigest (host);
}
// Option to write stage 1 image.
if (gDumpStage1.NotEmpty ())
{
dng_file_stream stream2 (gDumpStage1.Get (), true);
const dng_image &stage1 = *negative->Stage1Image ();
dng_image_writer writer;
writer.WriteTIFF (host,
stream2,
stage1,
stage1.Planes () >= 3 ? piRGB
: piBlackIsZero);
gDumpStage1.Clear ();
}
// Metadata.
negative->SynchronizeMetadata ();
// Four color Bayer option.
if (gFourColorBayer)
{
negative->SetFourColorBayer ();
}
// Build stage 2 image.
{
dng_timer timer ("Linearization time");
negative->BuildStage2Image (host);
}
if (gDumpStage2.NotEmpty ())
{
dng_file_stream stream2 (gDumpStage2.Get (), true);
const dng_image &stage2 = *negative->Stage2Image ();
dng_image_writer writer;
writer.WriteTIFF (host,
stream2,
stage2,
stage2.Planes () >= 3 ? piRGB
: piBlackIsZero);
gDumpStage2.Clear ();
}
// Build stage 3 image.
{
dng_timer timer ("Interpolate time");
negative->BuildStage3Image (host,
gMosaicPlane);
}
// Convert to proxy, if requested.
if (gProxyDNGSize)
{
dng_timer timer ("ConvertToProxy time");
dng_image_writer writer;
negative->ConvertToProxy (host,
writer,
gProxyDNGSize);
}
// Flatten transparency, if required.
if (negative->NeedFlattenTransparency (host))
{
dng_timer timer ("FlattenTransparency time");
negative->FlattenTransparency (host);
}
if (gDumpStage3.NotEmpty ())
{
dng_file_stream stream2 (gDumpStage3.Get (), true);
const dng_image &stage3 = *negative->Stage3Image ();
dng_image_writer writer;
writer.WriteTIFF (host,
stream2,
stage3,
stage3.Planes () >= 3 ? piRGB
: piBlackIsZero);
gDumpStage3.Clear ();
}
// Output DNG file if requested.
if (gDumpDNG.NotEmpty ())
{
// Build the preview list.
dng_preview_list previewList;
dng_date_time_info dateTimeInfo;
CurrentDateTimeAndZone (dateTimeInfo);
for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++)
{
// Skip preview if writing a compresssed main image to save space
// in this example code.
if (negative->RawJPEGImage () != NULL && previewIndex > 0)
{
break;
}
// Report timing.
dng_timer timer (previewIndex == 0 ? "Build thumbnail time"
: "Build preview time");
// Render a preview sized image.
AutoPtr<dng_image> previewImage;
{
dng_render render (host, *negative);
render.SetFinalSpace (negative->IsMonochrome () ? dng_space_GrayGamma22::Get ()
: dng_space_sRGB ::Get ());
render.SetFinalPixelType (ttByte);
render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
previewImage.Reset (render.Render ());
}
// Don't write the preview if it is same size as thumbnail.
if (previewIndex > 0 &&
Max_uint32 (previewImage->Bounds ().W (),
previewImage->Bounds ().H ()) <= 256)
{
break;
}
// If we have compressed JPEG data, create a compressed thumbnail. Otherwise
// save a uncompressed thumbnail.
bool useCompressedPreview = (negative->RawJPEGImage () != NULL) ||
(previewIndex > 0);
AutoPtr<dng_preview> preview (useCompressedPreview ?
(dng_preview *) new dng_jpeg_preview :
(dng_preview *) new dng_image_preview);
// Setup up preview info.
preview->fInfo.fApplicationName .Set ("dng_validate");
preview->fInfo.fApplicationVersion.Set (kDNGValidateVersion);
preview->fInfo.fSettingsName.Set ("Default");
preview->fInfo.fColorSpace = previewImage->Planes () == 1 ?
previewColorSpace_GrayGamma22 :
previewColorSpace_sRGB;
preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601 ();
if (!useCompressedPreview)
{
dng_image_preview *imagePreview = dynamic_cast<dng_image_preview *> (preview.Get ());
imagePreview->fImage.Reset (previewImage.Release ());
}
else
{
dng_jpeg_preview *jpegPreview = dynamic_cast<dng_jpeg_preview *> (preview.Get ());
int32 quality = (previewIndex == 0 ? 8 : 5);
dng_image_writer writer;
writer.EncodeJPEGPreview (host,
*previewImage,
*jpegPreview,
quality);
}
previewList.Append (preview);
}
// Write DNG file.
dng_file_stream stream2 (gDumpDNG.Get (), true);
{
dng_timer timer ("Write DNG time");
dng_image_writer writer;
writer.WriteDNG (host,
stream2,
*negative.Get (),
&previewList,
dngVersion_Current,
false);
}
gDumpDNG.Clear ();
}
// Output TIF file if requested.
if (gDumpTIF.NotEmpty ())
{
// Render final image.
dng_render render (host, *negative);
render.SetFinalSpace (*gFinalSpace );
render.SetFinalPixelType (gFinalPixelType);
if (host.MinimumSize ())
{
dng_point stage3Size = negative->Stage3Image ()->Size ();
render.SetMaximumSize (Max_uint32 (stage3Size.v,
stage3Size.h));
}
AutoPtr<dng_image> finalImage;
{
dng_timer timer ("Render time");
finalImage.Reset (render.Render ());
}
finalImage->Rotate (negative->Orientation ());
// Now that Camera Raw supports non-raw formats, we should
// not keep any Camera Raw settings in the XMP around when
// writing rendered files.
#if qDNGUseXMP
if (negative->GetXMP ())
{
negative->GetXMP ()->RemoveProperties (XMP_NS_CRS);
negative->GetXMP ()->RemoveProperties (XMP_NS_CRSS);
}
#endif
// Write TIF file.
dng_file_stream stream2 (gDumpTIF.Get (), true);
{
dng_timer timer ("Write TIFF time");
dng_image_writer writer;
writer.WriteTIFF (host,
stream2,
*finalImage.Get (),
finalImage->Planes () >= 3 ? piRGB
: piBlackIsZero,
ccUncompressed,
negative.Get (),
&render.FinalSpace ());
}
gDumpTIF.Clear ();
}
}
catch (const dng_exception &except)
{
return except.ErrorCode ();
}
catch (...)
{
return dng_error_unknown;
}
printf ("Validation complete\n");
return dng_error_none;
}
/*****************************************************************************/
int main (int argc, char *argv [])
{
try
{
if (argc == 1)
{
fprintf (stderr,
"\n"
"dng_validate, version " kDNGValidateVersion " "
#if qDNG64Bit
"(64-bit)"
#else
"(32-bit)"
#endif
"\n"
"Copyright 2005-2012 Adobe Systems, Inc.\n"
"\n"
"Usage: %s [options] file1 file2 ...\n"
"\n"
"Valid options:\n"
"-v Verbose mode\n"
"-d <num> Dump line limit (implies -v)\n"
"-b4 Use four-color Bayer interpolation\n"
"-s <num> Use this sample of multi-sample CFAs\n"
"-size <num> Preferred preview image size\n"
"-min <num> Minimum preview image size\n"
"-max <num> Maximum preview image size\n"
"-proxy <num> Target size for proxy DNG\n"
"-cs1 Color space: \"sRGB\" (default)\n"
"-cs2 Color space: \"Adobe RGB\"\n"
"-cs3 Color space: \"ProPhoto RGB\"\n"
"-cs4 Color space: \"ColorMatch RGB\"\n"
"-cs5 Color space: \"Gray Gamma 1.8\"\n"
"-cs6 Color space: \"Gray Gamma 2.2\"\n"
"-16 16-bits/channel output\n"
"-1 <file> Write stage 1 image to \"<file>.tif\"\n"
"-2 <file> Write stage 2 image to \"<file>.tif\"\n"
"-3 <file> Write stage 3 image to \"<file>.tif\"\n"
"-tif <file> Write TIF image to \"<file>.tif\"\n"
"-dng <file> Write DNG image to \"<file>.dng\"\n"
"\n",
argv [0]);
return 1;
}
int index;
for (index = 1; index < argc && argv [index] [0] == '-'; index++)
{
dng_string option;
option.Set (&argv [index] [1]);
if (option.Matches ("v", true))
{
gVerbose = true;
}
else if (option.Matches ("d", true))
{
gVerbose = true;
gDumpLineLimit = 0;
if (index + 1 < argc)
{
gDumpLineLimit = atoi (argv [++index]);
}
if (!gDumpLineLimit)
{
fprintf (stderr, "*** Invalid number after -d\n");
return 1;
}
}
else if (option.Matches ("s", true))
{
if (index + 1 < argc)
{
gMosaicPlane = atoi (argv [++index]);
}
else
{
fprintf (stderr, "*** Missing number after -s\n");
return 1;
}
}
else if (option.Matches ("b4", true))
{
gFourColorBayer = true;
}
else if (option.Matches ("size", true))
{
if (index + 1 < argc)
{
gPreferredSize = (uint32) atoi (argv [++index]);
}
else
{
fprintf (stderr, "*** Missing number after -size\n");
return 1;
}
}
else if (option.Matches ("min", true))
{
if (index + 1 < argc)
{
gMinimumSize = (uint32) atoi (argv [++index]);
}
else
{
fprintf (stderr, "*** Missing number after -min\n");
return 1;
}
}
else if (option.Matches ("max", true))
{
if (index + 1 < argc)
{
gMaximumSize = (uint32) atoi (argv [++index]);
}
else
{
fprintf (stderr, "*** Missing number after -max\n");
return 1;
}
}
else if (option.Matches ("proxy", true))
{
if (index + 1 < argc)
{
gProxyDNGSize = (uint32) atoi (argv [++index]);
}
else
{
fprintf (stderr, "*** Missing number after -proxy\n");
return 1;
}
}
else if (option.Matches ("cs1", true))
{
gFinalSpace = &dng_space_sRGB::Get ();
}
else if (option.Matches ("cs2", true))
{
gFinalSpace = &dng_space_AdobeRGB::Get ();
}
else if (option.Matches ("cs3", true))
{
gFinalSpace = &dng_space_ProPhoto::Get ();
}
else if (option.Matches ("cs4", true))
{
gFinalSpace = &dng_space_ColorMatch::Get ();
}
else if (option.Matches ("cs5", true))
{
gFinalSpace = &dng_space_GrayGamma18::Get ();
}
else if (option.Matches ("cs6", true))
{
gFinalSpace = &dng_space_GrayGamma22::Get ();
}
else if (option.Matches ("16"))
{
gFinalPixelType = ttShort;
}
else if (option.Matches ("1"))
{
gDumpStage1.Clear ();
if (index + 1 < argc)
{
gDumpStage1.Set (argv [++index]);
}
if (gDumpStage1.IsEmpty () || gDumpStage1.StartsWith ("-"))
{
fprintf (stderr, "*** Missing file name after -1\n");
return 1;
}
if (!gDumpStage1.EndsWith (".tif"))
{
gDumpStage1.Append (".tif");
}
}
else if (option.Matches ("2"))
{
gDumpStage2.Clear ();
if (index + 1 < argc)
{
gDumpStage2.Set (argv [++index]);
}
if (gDumpStage2.IsEmpty () || gDumpStage2.StartsWith ("-"))
{
fprintf (stderr, "*** Missing file name after -2\n");
return 1;
}
if (!gDumpStage2.EndsWith (".tif"))
{
gDumpStage2.Append (".tif");
}
}
else if (option.Matches ("3"))
{
gDumpStage3.Clear ();
if (index + 1 < argc)
{
gDumpStage3.Set (argv [++index]);
}
if (gDumpStage3.IsEmpty () || gDumpStage3.StartsWith ("-"))
{
fprintf (stderr, "*** Missing file name after -3\n");
return 1;
}
if (!gDumpStage3.EndsWith (".tif"))
{
gDumpStage3.Append (".tif");
}
}
else if (option.Matches ("tif", true))
{
gDumpTIF.Clear ();
if (index + 1 < argc)
{
gDumpTIF.Set (argv [++index]);
}
if (gDumpTIF.IsEmpty () || gDumpTIF.StartsWith ("-"))
{
fprintf (stderr, "*** Missing file name after -tif\n");
return 1;
}
if (!gDumpTIF.EndsWith (".tif"))
{
gDumpTIF.Append (".tif");
}
}
else if (option.Matches ("dng", true))
{
gDumpDNG.Clear ();
if (index + 1 < argc)
{
gDumpDNG.Set (argv [++index]);
}
if (gDumpDNG.IsEmpty () || gDumpDNG.StartsWith ("-"))
{
fprintf (stderr, "*** Missing file name after -dng\n");
return 1;
}
if (!gDumpDNG.EndsWith (".dng"))
{
gDumpDNG.Append (".dng");
}
}
else
{
fprintf (stderr, "*** Unknown option \"-%s\"\n", option.Get ());
return 1;
}
}
if (index == argc)
{
fprintf (stderr, "*** No file specified\n");
return 1;
}
#if qDNGUseXMP
dng_xmp_sdk::InitializeSDK ();
#endif
int result = 0;
while (index < argc)
{
dng_error_code error_code = dng_validate (argv [index++]);
if (error_code != dng_error_none)
{
result = error_code - dng_error_unknown + 100;
}
}
#if qDNGUseXMP
dng_xmp_sdk::TerminateSDK ();
#endif
return result;
}
catch (...)
{
}
fprintf (stderr, "*** Exception thrown in main routine\n");
return 1;
}
/*****************************************************************************/
#endif
/*****************************************************************************/