1/*****************************************************************************/
2// Copyright 2006-2012 Adobe Systems Incorporated
3// All Rights Reserved.
4//
5// NOTICE:  Adobe permits you to use, modify, and distribute this file in
6// accordance with the terms of the Adobe license agreement accompanying it.
7/*****************************************************************************/
8
9/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_validate.cpp#2 $ */
10/* $DateTime: 2012/06/14 20:24:41 $ */
11/* $Change: 835078 $ */
12/* $Author: tknoll $ */
13
14// Process exit codes
15// ------------------
16//
17// As usual, 0 indicates success.
18//
19// If an exception occurs, the exit code will be equal to:
20//
21//    DNG SDK error code - 100000 + 100
22//
23// For example, the error dng_error_memory, which has a DNG SDK error code of
24// 100005, is returned as an exit code of 105.
25//
26// This convention accounts for the fact that the shell truncates process exit
27// codes to 8 bits and that the exit code 1 is used by ASAN to signal that a
28// memory error occurred (so mapping the first DNG SDK error code to an exit
29// code of 1 would not be a good idea).
30
31/*****************************************************************************/
32
33#include "dng_color_space.h"
34#include "dng_date_time.h"
35#include "dng_exceptions.h"
36#include "dng_file_stream.h"
37#include "dng_globals.h"
38#include "dng_host.h"
39#include "dng_ifd.h"
40#include "dng_image_writer.h"
41#include "dng_info.h"
42#include "dng_linearization_info.h"
43#include "dng_mosaic_info.h"
44#include "dng_negative.h"
45#include "dng_preview.h"
46#include "dng_render.h"
47#include "dng_simple_image.h"
48#include "dng_tag_codes.h"
49#include "dng_tag_types.h"
50#include "dng_tag_values.h"
51
52#if qDNGUseXMP
53#include "dng_xmp.h"
54#include "dng_xmp_sdk.h"
55#endif
56
57/*****************************************************************************/
58
59#if qDNGValidateTarget
60
61/*****************************************************************************/
62
63#define kDNGValidateVersion "1.4"
64
65/*****************************************************************************/
66
67static bool gFourColorBayer = false;
68
69static int32 gMosaicPlane = -1;
70
71static uint32 gPreferredSize = 0;
72static uint32 gMinimumSize   = 0;
73static uint32 gMaximumSize   = 0;
74
75static uint32 gProxyDNGSize = 0;
76
77static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get ();
78
79static uint32 gFinalPixelType = ttByte;
80
81static dng_string gDumpStage1;
82static dng_string gDumpStage2;
83static dng_string gDumpStage3;
84static dng_string gDumpTIF;
85static dng_string gDumpDNG;
86
87/*****************************************************************************/
88
89static dng_error_code dng_validate (const char *filename)
90	{
91
92	printf ("Validating \"%s\"...\n", filename);
93
94	try
95		{
96
97		dng_file_stream stream (filename);
98
99		dng_host host;
100
101		host.SetPreferredSize (gPreferredSize);
102		host.SetMinimumSize   (gMinimumSize  );
103		host.SetMaximumSize   (gMaximumSize  );
104
105		host.ValidateSizes ();
106
107		if (host.MinimumSize ())
108			{
109
110			host.SetForPreview (true);
111
112			gDumpDNG.Clear ();
113
114			}
115
116		if (gDumpDNG.NotEmpty ())
117			{
118
119			host.SetSaveDNGVersion (dngVersion_SaveDefault);
120
121			host.SetSaveLinearDNG (false);
122
123			host.SetKeepOriginalFile (false);
124
125			}
126
127		// Read into the negative.
128
129		AutoPtr<dng_negative> negative;
130
131			{
132
133			dng_info info;
134
135			info.Parse (host, stream);
136
137			info.PostParse (host);
138
139			if (!info.IsValidDNG ())
140				{
141				return dng_error_bad_format;
142				}
143
144			negative.Reset (host.Make_dng_negative ());
145
146			negative->Parse (host, stream, info);
147
148			negative->PostParse (host, stream, info);
149
150				{
151
152				dng_timer timer ("Raw image read time");
153
154				negative->ReadStage1Image (host, stream, info);
155
156				}
157
158			if (info.fMaskIndex != -1)
159				{
160
161				dng_timer timer ("Transparency mask read time");
162
163				negative->ReadTransparencyMask (host, stream, info);
164
165				}
166
167			negative->ValidateRawImageDigest (host);
168
169			}
170
171		// Option to write stage 1 image.
172
173		if (gDumpStage1.NotEmpty ())
174			{
175
176			dng_file_stream stream2 (gDumpStage1.Get (), true);
177
178			const dng_image &stage1 = *negative->Stage1Image ();
179
180			dng_image_writer writer;
181
182			writer.WriteTIFF (host,
183							  stream2,
184							  stage1,
185							  stage1.Planes () >= 3 ? piRGB
186												    : piBlackIsZero);
187
188			gDumpStage1.Clear ();
189
190			}
191
192		// Metadata.
193
194		negative->SynchronizeMetadata ();
195
196		// Four color Bayer option.
197
198		if (gFourColorBayer)
199			{
200			negative->SetFourColorBayer ();
201			}
202
203		// Build stage 2 image.
204
205			{
206
207			dng_timer timer ("Linearization time");
208
209			negative->BuildStage2Image (host);
210
211			}
212
213		if (gDumpStage2.NotEmpty ())
214			{
215
216			dng_file_stream stream2 (gDumpStage2.Get (), true);
217
218			const dng_image &stage2 = *negative->Stage2Image ();
219
220			dng_image_writer writer;
221
222			writer.WriteTIFF (host,
223							  stream2,
224							  stage2,
225							  stage2.Planes () >= 3 ? piRGB
226												    : piBlackIsZero);
227
228			gDumpStage2.Clear ();
229
230			}
231
232		// Build stage 3 image.
233
234			{
235
236			dng_timer timer ("Interpolate time");
237
238			negative->BuildStage3Image (host,
239									    gMosaicPlane);
240
241			}
242
243		// Convert to proxy, if requested.
244
245		if (gProxyDNGSize)
246			{
247
248			dng_timer timer ("ConvertToProxy time");
249
250			dng_image_writer writer;
251
252			negative->ConvertToProxy (host,
253									  writer,
254									  gProxyDNGSize);
255
256			}
257
258		// Flatten transparency, if required.
259
260		if (negative->NeedFlattenTransparency (host))
261			{
262
263			dng_timer timer ("FlattenTransparency time");
264
265			negative->FlattenTransparency (host);
266
267			}
268
269		if (gDumpStage3.NotEmpty ())
270			{
271
272			dng_file_stream stream2 (gDumpStage3.Get (), true);
273
274			const dng_image &stage3 = *negative->Stage3Image ();
275
276			dng_image_writer writer;
277
278			writer.WriteTIFF (host,
279							  stream2,
280							  stage3,
281							  stage3.Planes () >= 3 ? piRGB
282												    : piBlackIsZero);
283
284			gDumpStage3.Clear ();
285
286			}
287
288		// Output DNG file if requested.
289
290		if (gDumpDNG.NotEmpty ())
291			{
292
293			// Build the preview list.
294
295			dng_preview_list previewList;
296
297			dng_date_time_info dateTimeInfo;
298
299			CurrentDateTimeAndZone (dateTimeInfo);
300
301			for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++)
302				{
303
304				// Skip preview if writing a compresssed main image to save space
305				// in this example code.
306
307				if (negative->RawJPEGImage () != NULL && previewIndex > 0)
308					{
309					break;
310					}
311
312				// Report timing.
313
314				dng_timer timer (previewIndex == 0 ? "Build thumbnail time"
315												   : "Build preview time");
316
317				// Render a preview sized image.
318
319				AutoPtr<dng_image> previewImage;
320
321					{
322
323					dng_render render (host, *negative);
324
325					render.SetFinalSpace (negative->IsMonochrome () ? dng_space_GrayGamma22::Get ()
326																	: dng_space_sRGB       ::Get ());
327
328					render.SetFinalPixelType (ttByte);
329
330					render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
331
332					previewImage.Reset (render.Render ());
333
334					}
335
336				// Don't write the preview if it is same size as thumbnail.
337
338				if (previewIndex > 0 &&
339					Max_uint32 (previewImage->Bounds ().W (),
340								previewImage->Bounds ().H ()) <= 256)
341					{
342					break;
343					}
344
345				// If we have compressed JPEG data, create a compressed thumbnail.  Otherwise
346				// save a uncompressed thumbnail.
347
348				bool useCompressedPreview = (negative->RawJPEGImage () != NULL) ||
349											(previewIndex > 0);
350
351				AutoPtr<dng_preview> preview (useCompressedPreview ?
352											  (dng_preview *) new dng_jpeg_preview :
353											  (dng_preview *) new dng_image_preview);
354
355				// Setup up preview info.
356
357				preview->fInfo.fApplicationName   .Set ("dng_validate");
358				preview->fInfo.fApplicationVersion.Set (kDNGValidateVersion);
359
360				preview->fInfo.fSettingsName.Set ("Default");
361
362				preview->fInfo.fColorSpace = previewImage->Planes () == 1 ?
363											 previewColorSpace_GrayGamma22 :
364											 previewColorSpace_sRGB;
365
366				preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601 ();
367
368				if (!useCompressedPreview)
369					{
370
371					dng_image_preview *imagePreview = dynamic_cast<dng_image_preview *> (preview.Get ());
372
373					imagePreview->fImage.Reset (previewImage.Release ());
374
375					}
376
377				else
378					{
379
380					dng_jpeg_preview *jpegPreview = dynamic_cast<dng_jpeg_preview *> (preview.Get ());
381
382					int32 quality = (previewIndex == 0 ? 8 : 5);
383
384					dng_image_writer writer;
385
386					writer.EncodeJPEGPreview (host,
387										      *previewImage,
388										      *jpegPreview,
389											  quality);
390
391					}
392
393				previewList.Append (preview);
394
395				}
396
397			// Write DNG file.
398
399			dng_file_stream stream2 (gDumpDNG.Get (), true);
400
401				{
402
403				dng_timer timer ("Write DNG time");
404
405				dng_image_writer writer;
406
407				writer.WriteDNG (host,
408								 stream2,
409								 *negative.Get (),
410								 &previewList,
411								 dngVersion_Current,
412								 false);
413
414				}
415
416			gDumpDNG.Clear ();
417
418			}
419
420		// Output TIF file if requested.
421
422		if (gDumpTIF.NotEmpty ())
423			{
424
425			// Render final image.
426
427			dng_render render (host, *negative);
428
429			render.SetFinalSpace     (*gFinalSpace   );
430			render.SetFinalPixelType (gFinalPixelType);
431
432			if (host.MinimumSize ())
433				{
434
435				dng_point stage3Size = negative->Stage3Image ()->Size ();
436
437				render.SetMaximumSize (Max_uint32 (stage3Size.v,
438												   stage3Size.h));
439
440				}
441
442			AutoPtr<dng_image> finalImage;
443
444				{
445
446				dng_timer timer ("Render time");
447
448				finalImage.Reset (render.Render ());
449
450				}
451
452			finalImage->Rotate (negative->Orientation ());
453
454			// Now that Camera Raw supports non-raw formats, we should
455			// not keep any Camera Raw settings in the XMP around when
456			// writing rendered files.
457
458			#if qDNGUseXMP
459
460			if (negative->GetXMP ())
461				{
462
463				negative->GetXMP ()->RemoveProperties (XMP_NS_CRS);
464				negative->GetXMP ()->RemoveProperties (XMP_NS_CRSS);
465
466				}
467
468			#endif
469
470			// Write TIF file.
471
472			dng_file_stream stream2 (gDumpTIF.Get (), true);
473
474				{
475
476				dng_timer timer ("Write TIFF time");
477
478				dng_image_writer writer;
479
480				writer.WriteTIFF (host,
481								  stream2,
482								  *finalImage.Get (),
483								  finalImage->Planes () >= 3 ? piRGB
484															 : piBlackIsZero,
485								  ccUncompressed,
486								  negative.Get (),
487								  &render.FinalSpace ());
488
489				}
490
491			gDumpTIF.Clear ();
492
493			}
494
495		}
496
497	catch (const dng_exception &except)
498		{
499
500		return except.ErrorCode ();
501
502		}
503
504	catch (...)
505		{
506
507		return dng_error_unknown;
508
509		}
510
511	printf ("Validation complete\n");
512
513	return dng_error_none;
514
515	}
516
517/*****************************************************************************/
518
519int main (int argc, char *argv [])
520	{
521
522	try
523		{
524
525		if (argc == 1)
526			{
527
528			fprintf (stderr,
529					 "\n"
530					 "dng_validate, version " kDNGValidateVersion " "
531					 #if qDNG64Bit
532					 "(64-bit)"
533					 #else
534					 "(32-bit)"
535					 #endif
536					 "\n"
537					 "Copyright 2005-2012 Adobe Systems, Inc.\n"
538					 "\n"
539					 "Usage:  %s [options] file1 file2 ...\n"
540					 "\n"
541					 "Valid options:\n"
542					 "-v            Verbose mode\n"
543					 "-d <num>      Dump line limit (implies -v)\n"
544					 "-b4           Use four-color Bayer interpolation\n"
545					 "-s <num>      Use this sample of multi-sample CFAs\n"
546					 "-size <num>   Preferred preview image size\n"
547					 "-min <num>    Minimum preview image size\n"
548					 "-max <num>    Maximum preview image size\n"
549					 "-proxy <num>  Target size for proxy DNG\n"
550					 "-cs1          Color space: \"sRGB\" (default)\n"
551					 "-cs2          Color space: \"Adobe RGB\"\n"
552					 "-cs3          Color space: \"ProPhoto RGB\"\n"
553					 "-cs4          Color space: \"ColorMatch RGB\"\n"
554					 "-cs5          Color space: \"Gray Gamma 1.8\"\n"
555					 "-cs6          Color space: \"Gray Gamma 2.2\"\n"
556					 "-16           16-bits/channel output\n"
557					 "-1 <file>     Write stage 1 image to \"<file>.tif\"\n"
558					 "-2 <file>     Write stage 2 image to \"<file>.tif\"\n"
559					 "-3 <file>     Write stage 3 image to \"<file>.tif\"\n"
560					 "-tif <file>   Write TIF image to \"<file>.tif\"\n"
561					 "-dng <file>   Write DNG image to \"<file>.dng\"\n"
562					 "\n",
563					 argv [0]);
564
565			return 1;
566
567			}
568
569		int index;
570
571		for (index = 1; index < argc && argv [index] [0] == '-'; index++)
572			{
573
574			dng_string option;
575
576			option.Set (&argv [index] [1]);
577
578			if (option.Matches ("v", true))
579				{
580				gVerbose = true;
581				}
582
583			else if (option.Matches ("d", true))
584				{
585
586				gVerbose = true;
587
588				gDumpLineLimit = 0;
589
590				if (index + 1 < argc)
591					{
592					gDumpLineLimit = atoi (argv [++index]);
593					}
594
595				if (!gDumpLineLimit)
596					{
597					fprintf (stderr, "*** Invalid number after -d\n");
598					return 1;
599					}
600
601				}
602
603			else if (option.Matches ("s", true))
604				{
605
606				if (index + 1 < argc)
607					{
608					gMosaicPlane = atoi (argv [++index]);
609					}
610
611				else
612					{
613					fprintf (stderr, "*** Missing number after -s\n");
614					return 1;
615					}
616
617				}
618
619			else if (option.Matches ("b4", true))
620				{
621				gFourColorBayer = true;
622				}
623
624			else if (option.Matches ("size", true))
625				{
626
627				if (index + 1 < argc)
628					{
629					gPreferredSize = (uint32) atoi (argv [++index]);
630					}
631
632				else
633					{
634					fprintf (stderr, "*** Missing number after -size\n");
635					return 1;
636					}
637
638				}
639
640			else if (option.Matches ("min", true))
641				{
642
643				if (index + 1 < argc)
644					{
645					gMinimumSize = (uint32) atoi (argv [++index]);
646					}
647
648				else
649					{
650					fprintf (stderr, "*** Missing number after -min\n");
651					return 1;
652					}
653
654				}
655
656			else if (option.Matches ("max", true))
657				{
658
659				if (index + 1 < argc)
660					{
661					gMaximumSize = (uint32) atoi (argv [++index]);
662					}
663
664				else
665					{
666					fprintf (stderr, "*** Missing number after -max\n");
667					return 1;
668					}
669
670				}
671
672			else if (option.Matches ("proxy", true))
673				{
674
675				if (index + 1 < argc)
676					{
677					gProxyDNGSize = (uint32) atoi (argv [++index]);
678					}
679
680				else
681					{
682					fprintf (stderr, "*** Missing number after -proxy\n");
683					return 1;
684					}
685
686				}
687
688			else if (option.Matches ("cs1", true))
689				{
690
691				gFinalSpace = &dng_space_sRGB::Get ();
692
693				}
694
695			else if (option.Matches ("cs2", true))
696				{
697
698				gFinalSpace = &dng_space_AdobeRGB::Get ();
699
700				}
701
702			else if (option.Matches ("cs3", true))
703				{
704
705				gFinalSpace = &dng_space_ProPhoto::Get ();
706
707				}
708
709			else if (option.Matches ("cs4", true))
710				{
711
712				gFinalSpace = &dng_space_ColorMatch::Get ();
713
714				}
715
716			else if (option.Matches ("cs5", true))
717				{
718
719				gFinalSpace = &dng_space_GrayGamma18::Get ();
720
721				}
722
723			else if (option.Matches ("cs6", true))
724				{
725
726				gFinalSpace = &dng_space_GrayGamma22::Get ();
727
728				}
729
730			else if (option.Matches ("16"))
731				{
732
733				gFinalPixelType = ttShort;
734
735				}
736
737			else if (option.Matches ("1"))
738				{
739
740				gDumpStage1.Clear ();
741
742				if (index + 1 < argc)
743					{
744					gDumpStage1.Set (argv [++index]);
745					}
746
747				if (gDumpStage1.IsEmpty () || gDumpStage1.StartsWith ("-"))
748					{
749					fprintf (stderr, "*** Missing file name after -1\n");
750					return 1;
751					}
752
753				if (!gDumpStage1.EndsWith (".tif"))
754					{
755					gDumpStage1.Append (".tif");
756					}
757
758				}
759
760			else if (option.Matches ("2"))
761				{
762
763				gDumpStage2.Clear ();
764
765				if (index + 1 < argc)
766					{
767					gDumpStage2.Set (argv [++index]);
768					}
769
770				if (gDumpStage2.IsEmpty () || gDumpStage2.StartsWith ("-"))
771					{
772					fprintf (stderr, "*** Missing file name after -2\n");
773					return 1;
774					}
775
776				if (!gDumpStage2.EndsWith (".tif"))
777					{
778					gDumpStage2.Append (".tif");
779					}
780
781				}
782
783			else if (option.Matches ("3"))
784				{
785
786				gDumpStage3.Clear ();
787
788				if (index + 1 < argc)
789					{
790					gDumpStage3.Set (argv [++index]);
791					}
792
793				if (gDumpStage3.IsEmpty () || gDumpStage3.StartsWith ("-"))
794					{
795					fprintf (stderr, "*** Missing file name after -3\n");
796					return 1;
797					}
798
799				if (!gDumpStage3.EndsWith (".tif"))
800					{
801					gDumpStage3.Append (".tif");
802					}
803
804				}
805
806			else if (option.Matches ("tif", true))
807				{
808
809				gDumpTIF.Clear ();
810
811				if (index + 1 < argc)
812					{
813					gDumpTIF.Set (argv [++index]);
814					}
815
816				if (gDumpTIF.IsEmpty () || gDumpTIF.StartsWith ("-"))
817					{
818					fprintf (stderr, "*** Missing file name after -tif\n");
819					return 1;
820					}
821
822				if (!gDumpTIF.EndsWith (".tif"))
823					{
824					gDumpTIF.Append (".tif");
825					}
826
827				}
828
829			else if (option.Matches ("dng", true))
830				{
831
832				gDumpDNG.Clear ();
833
834				if (index + 1 < argc)
835					{
836					gDumpDNG.Set (argv [++index]);
837					}
838
839				if (gDumpDNG.IsEmpty () || gDumpDNG.StartsWith ("-"))
840					{
841					fprintf (stderr, "*** Missing file name after -dng\n");
842					return 1;
843					}
844
845				if (!gDumpDNG.EndsWith (".dng"))
846					{
847					gDumpDNG.Append (".dng");
848					}
849
850				}
851
852			else
853				{
854				fprintf (stderr, "*** Unknown option \"-%s\"\n", option.Get ());
855				return 1;
856				}
857
858			}
859
860		if (index == argc)
861			{
862			fprintf (stderr, "*** No file specified\n");
863			return 1;
864			}
865
866		#if qDNGUseXMP
867
868		dng_xmp_sdk::InitializeSDK ();
869
870		#endif
871
872		int result = 0;
873
874		while (index < argc)
875			{
876
877			dng_error_code error_code = dng_validate (argv [index++]);
878			if (error_code != dng_error_none)
879				{
880
881				result = error_code - dng_error_unknown + 100;
882
883				}
884
885			}
886
887		#if qDNGUseXMP
888
889		dng_xmp_sdk::TerminateSDK ();
890
891		#endif
892
893		return result;
894
895		}
896
897	catch (...)
898		{
899
900		}
901
902	fprintf (stderr, "*** Exception thrown in main routine\n");
903
904	return 1;
905
906	}
907
908/*****************************************************************************/
909
910#endif
911
912/*****************************************************************************/
913