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
67 static bool gFourColorBayer = false;
68
69 static int32 gMosaicPlane = -1;
70
71 static uint32 gPreferredSize = 0;
72 static uint32 gMinimumSize = 0;
73 static uint32 gMaximumSize = 0;
74
75 static uint32 gProxyDNGSize = 0;
76
77 static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get ();
78
79 static uint32 gFinalPixelType = ttByte;
80
81 static dng_string gDumpStage1;
82 static dng_string gDumpStage2;
83 static dng_string gDumpStage3;
84 static dng_string gDumpTIF;
85 static dng_string gDumpDNG;
86
87 /*****************************************************************************/
88
dng_validate(const char * filename)89 static 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
main(int argc,char * argv[])519 int 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