| /*****************************************************************************/ |
| // 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 |
| |
| /*****************************************************************************/ |