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_image_writer.cpp#4 $ */
10/* $DateTime: 2012/06/14 20:24:41 $ */
11/* $Change: 835078 $ */
12/* $Author: tknoll $ */
13
14/*****************************************************************************/
15
16#include "dng_image_writer.h"
17
18#include "dng_abort_sniffer.h"
19#include "dng_area_task.h"
20#include "dng_bottlenecks.h"
21#include "dng_camera_profile.h"
22#include "dng_color_space.h"
23#include "dng_exif.h"
24#include "dng_flags.h"
25#include "dng_exceptions.h"
26#include "dng_host.h"
27#include "dng_ifd.h"
28#include "dng_image.h"
29#include "dng_jpeg_image.h"
30#include "dng_lossless_jpeg.h"
31#include "dng_memory.h"
32#include "dng_memory_stream.h"
33#include "dng_negative.h"
34#include "dng_pixel_buffer.h"
35#include "dng_preview.h"
36#include "dng_read_image.h"
37#include "dng_safe_arithmetic.h"
38#include "dng_stream.h"
39#include "dng_string_list.h"
40#include "dng_tag_codes.h"
41#include "dng_tag_values.h"
42#include "dng_utils.h"
43
44#if qDNGUseXMP
45#include "dng_xmp.h"
46#endif
47
48#include "zlib.h"
49
50#if qDNGUseLibJPEG
51#include "dng_jpeglib.h"
52#endif
53
54/*****************************************************************************/
55
56// Defines for testing DNG 1.2 features.
57
58//#define qTestRowInterleave 2
59
60//#define qTestSubTileBlockRows 2
61//#define qTestSubTileBlockCols 2
62
63/*****************************************************************************/
64
65dng_resolution::dng_resolution ()
66
67 : fXResolution ()
68 , fYResolution ()
69
70 , fResolutionUnit (0)
71
72 {
73
74 }
75
76/******************************************************************************/
77
78static void SpoolAdobeData (dng_stream &stream,
79 const dng_metadata *metadata,
80 const dng_jpeg_preview *preview,
81 const dng_memory_block *imageResources)
82 {
83
84 TempBigEndian tempEndian (stream);
85
86 #if qDNGUseXMP
87
88 if (metadata && metadata->GetXMP ())
89 {
90
91 bool marked = false;
92
93 if (metadata->GetXMP ()->GetBoolean (XMP_NS_XAP_RIGHTS,
94 "Marked",
95 marked))
96 {
97
98 stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
99 stream.Put_uint16 (1034);
100 stream.Put_uint16 (0);
101
102 stream.Put_uint32 (1);
103
104 stream.Put_uint8 (marked ? 1 : 0);
105
106 stream.Put_uint8 (0);
107
108 }
109
110 dng_string webStatement;
111
112 if (metadata->GetXMP ()->GetString (XMP_NS_XAP_RIGHTS,
113 "WebStatement",
114 webStatement))
115 {
116
117 dng_memory_data buffer;
118
119 uint32 size = webStatement.Get_SystemEncoding (buffer);
120
121 if (size > 0)
122 {
123
124 stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
125 stream.Put_uint16 (1035);
126 stream.Put_uint16 (0);
127
128 stream.Put_uint32 (size);
129
130 stream.Put (buffer.Buffer (), size);
131
132 if (size & 1)
133 stream.Put_uint8 (0);
134
135 }
136
137 }
138
139 }
140
141 #endif
142
143 if (preview)
144 {
145
146 preview->SpoolAdobeThumbnail (stream);
147
148 }
149
150 if (metadata && metadata->IPTCLength ())
151 {
152
153 dng_fingerprint iptcDigest = metadata->IPTCDigest ();
154
155 if (iptcDigest.IsValid ())
156 {
157
158 stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
159 stream.Put_uint16 (1061);
160 stream.Put_uint16 (0);
161
162 stream.Put_uint32 (16);
163
164 stream.Put (iptcDigest.data, 16);
165
166 }
167
168 }
169
170 if (imageResources)
171 {
172
173 uint32 size = imageResources->LogicalSize ();
174
175 stream.Put (imageResources->Buffer (), size);
176
177 if (size & 1)
178 stream.Put_uint8 (0);
179
180 }
181
182 }
183
184/******************************************************************************/
185
186static dng_memory_block * BuildAdobeData (dng_host &host,
187 const dng_metadata *metadata,
188 const dng_jpeg_preview *preview,
189 const dng_memory_block *imageResources)
190 {
191
192 dng_memory_stream stream (host.Allocator ());
193
194 SpoolAdobeData (stream,
195 metadata,
196 preview,
197 imageResources);
198
199 return stream.AsMemoryBlock (host.Allocator ());
200
201 }
202
203/*****************************************************************************/
204
205tag_string::tag_string (uint16 code,
206 const dng_string &s,
207 bool forceASCII)
208
209 : tiff_tag (code, ttAscii, 0)
210
211 , fString (s)
212
213 {
214
215 if (forceASCII)
216 {
217
218 // Metadata working group recommendation - go ahead
219 // write UTF-8 into ASCII tag strings, rather than
220 // actually force the strings to ASCII. There is a matching
221 // change on the reading side to assume UTF-8 if the string
222 // contains a valid UTF-8 string.
223 //
224 // fString.ForceASCII ();
225
226 }
227
228 else if (!fString.IsASCII ())
229 {
230
231 fType = ttByte;
232
233 }
234
235 fCount = fString.Length () + 1;
236
237 }
238
239/*****************************************************************************/
240
241void tag_string::Put (dng_stream &stream) const
242 {
243
244 stream.Put (fString.Get (), Size ());
245
246 }
247
248/*****************************************************************************/
249
250tag_encoded_text::tag_encoded_text (uint16 code,
251 const dng_string &text)
252
253 : tiff_tag (code, ttUndefined, 0)
254
255 , fText (text)
256
257 , fUTF16 ()
258
259 {
260
261 if (fText.IsASCII ())
262 {
263
264 fCount = 8 + fText.Length ();
265
266 }
267
268 else
269 {
270
271 fCount = 8 + fText.Get_UTF16 (fUTF16) * 2;
272
273 }
274
275 }
276
277/*****************************************************************************/
278
279void tag_encoded_text::Put (dng_stream &stream) const
280 {
281
282 if (fUTF16.Buffer ())
283 {
284
285 stream.Put ("UNICODE\000", 8);
286
287 uint32 chars = (fCount - 8) >> 1;
288
289 const uint16 *buf = fUTF16.Buffer_uint16 ();
290
291 for (uint32 j = 0; j < chars; j++)
292 {
293
294 stream.Put_uint16 (buf [j]);
295
296 }
297
298 }
299
300 else
301 {
302
303 stream.Put ("ASCII\000\000\000", 8);
304
305 stream.Put (fText.Get (), fCount - 8);
306
307 }
308
309 }
310
311/*****************************************************************************/
312
313void tag_data_ptr::Put (dng_stream &stream) const
314 {
315
316 // If we are swapping bytes, we need to swap with the right size
317 // entries.
318
319 if (stream.SwapBytes ())
320 {
321
322 switch (Type ())
323 {
324
325 // Two byte entries.
326
327 case ttShort:
328 case ttSShort:
329 case ttUnicode:
330 {
331
332 const uint16 *p = (const uint16 *) fData;
333
334 uint32 entries = (Size () >> 1);
335
336 for (uint32 j = 0; j < entries; j++)
337 {
338
339 stream.Put_uint16 (p [j]);
340
341 }
342
343 return;
344
345 }
346
347 // Four byte entries.
348
349 case ttLong:
350 case ttSLong:
351 case ttRational:
352 case ttSRational:
353 case ttIFD:
354 case ttFloat:
355 case ttComplex:
356 {
357
358 const uint32 *p = (const uint32 *) fData;
359
360 uint32 entries = (Size () >> 2);
361
362 for (uint32 j = 0; j < entries; j++)
363 {
364
365 stream.Put_uint32 (p [j]);
366
367 }
368
369 return;
370
371 }
372
373 // Eight byte entries.
374
375 case ttDouble:
376 {
377
378 const real64 *p = (const real64 *) fData;
379
380 uint32 entries = (Size () >> 3);
381
382 for (uint32 j = 0; j < entries; j++)
383 {
384
385 stream.Put_real64 (p [j]);
386
387 }
388
389 return;
390
391 }
392
393 // Entries don't need to be byte swapped. Fall through
394 // to non-byte swapped case.
395
396 default:
397 {
398
399 break;
400
401 }
402
403 }
404
405 }
406
407 // Non-byte swapped case.
408
409 stream.Put (fData, Size ());
410
411 }
412
413/******************************************************************************/
414
415tag_matrix::tag_matrix (uint16 code,
416 const dng_matrix &m)
417
418 : tag_srational_ptr (code, fEntry, m.Rows () * m.Cols ())
419
420 {
421
422 uint32 index = 0;
423
424 for (uint32 r = 0; r < m.Rows (); r++)
425 for (uint32 c = 0; c < m.Cols (); c++)
426 {
427
428 fEntry [index].Set_real64 (m [r] [c], 10000);
429
430 index++;
431
432 }
433
434 }
435
436/******************************************************************************/
437
438tag_icc_profile::tag_icc_profile (const void *profileData,
439 uint32 profileSize)
440
441 : tag_data_ptr (tcICCProfile,
442 ttUndefined,
443 0,
444 NULL)
445
446 {
447
448 if (profileData && profileSize)
449 {
450
451 SetCount (profileSize);
452 SetData (profileData);
453
454 }
455
456 }
457
458/******************************************************************************/
459
460void tag_cfa_pattern::Put (dng_stream &stream) const
461 {
462
463 stream.Put_uint16 ((uint16) fCols);
464 stream.Put_uint16 ((uint16) fRows);
465
466 for (uint32 col = 0; col < fCols; col++)
467 for (uint32 row = 0; row < fRows; row++)
468 {
469
470 stream.Put_uint8 (fPattern [row * kMaxCFAPattern + col]);
471
472 }
473
474 }
475
476/******************************************************************************/
477
478tag_exif_date_time::tag_exif_date_time (uint16 code,
479 const dng_date_time &dt)
480
481 : tag_data_ptr (code, ttAscii, 20, fData)
482
483 {
484
485 if (dt.IsValid ())
486 {
487
488 sprintf (fData,
489 "%04d:%02d:%02d %02d:%02d:%02d",
490 (int) dt.fYear,
491 (int) dt.fMonth,
492 (int) dt.fDay,
493 (int) dt.fHour,
494 (int) dt.fMinute,
495 (int) dt.fSecond);
496
497 }
498
499 }
500
501/******************************************************************************/
502
503tag_iptc::tag_iptc (const void *data,
504 uint32 length)
505
506 : tiff_tag (tcIPTC_NAA, ttLong, (length + 3) >> 2)
507
508 , fData (data )
509 , fLength (length)
510
511 {
512
513 }
514
515/******************************************************************************/
516
517void tag_iptc::Put (dng_stream &stream) const
518 {
519
520 // Note: For historical compatiblity reasons, the standard TIFF data
521 // type for IPTC data is ttLong, but without byte swapping. This really
522 // should be ttUndefined, but doing the right thing would break some
523 // existing readers.
524
525 stream.Put (fData, fLength);
526
527 // Pad with zeros to get to long word boundary.
528
529 uint32 extra = fCount * 4 - fLength;
530
531 while (extra--)
532 {
533 stream.Put_uint8 (0);
534 }
535
536 }
537
538/******************************************************************************/
539
540tag_xmp::tag_xmp (const dng_xmp *xmp)
541
542 : tag_uint8_ptr (tcXMP, NULL, 0)
543
544 , fBuffer ()
545
546 {
547
548 #if qDNGUseXMP
549
550 if (xmp)
551 {
552
553 fBuffer.Reset (xmp->Serialize (true));
554
555 if (fBuffer.Get ())
556 {
557
558 SetData (fBuffer->Buffer_uint8 ());
559
560 SetCount (fBuffer->LogicalSize ());
561
562 }
563
564 }
565
566 #endif
567
568 }
569
570/******************************************************************************/
571
572void dng_tiff_directory::Add (const tiff_tag *tag)
573 {
574
575 if (fEntries >= kMaxEntries)
576 {
577 ThrowProgramError ();
578 }
579
580 // Tags must be sorted in increasing order of tag code.
581
582 uint32 index = fEntries;
583
584 for (uint32 j = 0; j < fEntries; j++)
585 {
586
587 if (tag->Code () < fTag [j]->Code ())
588 {
589 index = j;
590 break;
591 }
592
593 }
594
595 for (uint32 k = fEntries; k > index; k--)
596 {
597
598 fTag [k] = fTag [k - 1];
599
600 }
601
602 fTag [index] = tag;
603
604 fEntries++;
605
606 }
607
608/******************************************************************************/
609
610uint32 dng_tiff_directory::Size () const
611 {
612
613 if (!fEntries) return 0;
614
615 uint32 size = fEntries * 12 + 6;
616
617 for (uint32 index = 0; index < fEntries; index++)
618 {
619
620 uint32 tagSize = fTag [index]->Size ();
621
622 if (tagSize > 4)
623 {
624
625 size += (tagSize + 1) & ~1;
626
627 }
628
629 }
630
631 return size;
632
633 }
634
635/******************************************************************************/
636
637void dng_tiff_directory::Put (dng_stream &stream,
638 OffsetsBase offsetsBase,
639 uint32 explicitBase) const
640 {
641
642 if (!fEntries) return;
643
644 uint32 index;
645
646 uint32 bigData = fEntries * 12 + 6;
647
648 if (offsetsBase == offsetsRelativeToStream)
649 bigData += (uint32) stream.Position ();
650
651 else if (offsetsBase == offsetsRelativeToExplicitBase)
652 bigData += explicitBase;
653
654 stream.Put_uint16 ((uint16) fEntries);
655
656 for (index = 0; index < fEntries; index++)
657 {
658
659 const tiff_tag &tag = *fTag [index];
660
661 stream.Put_uint16 (tag.Code ());
662 stream.Put_uint16 (tag.Type ());
663 stream.Put_uint32 (tag.Count ());
664
665 uint32 size = tag.Size ();
666
667 if (size <= 4)
668 {
669
670 tag.Put (stream);
671
672 while (size < 4)
673 {
674 stream.Put_uint8 (0);
675 size++;
676 }
677
678 }
679
680 else
681 {
682
683 stream.Put_uint32 (bigData);
684
685 bigData += (size + 1) & ~1;
686
687 }
688
689 }
690
691 stream.Put_uint32 (fChained); // Next IFD offset
692
693 for (index = 0; index < fEntries; index++)
694 {
695
696 const tiff_tag &tag = *fTag [index];
697
698 uint32 size = tag.Size ();
699
700 if (size > 4)
701 {
702
703 tag.Put (stream);
704
705 if (size & 1)
706 stream.Put_uint8 (0);
707
708 }
709
710 }
711
712 }
713
714/******************************************************************************/
715
716dng_basic_tag_set::dng_basic_tag_set (dng_tiff_directory &directory,
717 const dng_ifd &info)
718
719 : fNewSubFileType (tcNewSubFileType, info.fNewSubFileType)
720
721 , fImageWidth (tcImageWidth , info.fImageWidth )
722 , fImageLength (tcImageLength, info.fImageLength)
723
724 , fPhotoInterpretation (tcPhotometricInterpretation,
725 (uint16) info.fPhotometricInterpretation)
726
727 , fFillOrder (tcFillOrder, 1)
728
729 , fSamplesPerPixel (tcSamplesPerPixel, (uint16) info.fSamplesPerPixel)
730
731 , fBitsPerSample (tcBitsPerSample,
732 fBitsPerSampleData,
733 info.fSamplesPerPixel)
734
735 , fStrips (info.fUsesStrips)
736
737 , fTileWidth (tcTileWidth, info.fTileWidth)
738
739 , fTileLength (fStrips ? tcRowsPerStrip : tcTileLength,
740 info.fTileLength)
741
742 , fTileInfoBuffer (info.TilesPerImage (), 8)
743
744 , fTileOffsetData (fTileInfoBuffer.Buffer_uint32 ())
745
746 , fTileOffsets (fStrips ? tcStripOffsets : tcTileOffsets,
747 fTileOffsetData,
748 info.TilesPerImage ())
749
750 , fTileByteCountData (fTileOffsetData + info.TilesPerImage ())
751
752 , fTileByteCounts (fStrips ? tcStripByteCounts : tcTileByteCounts,
753 fTileByteCountData,
754 info.TilesPerImage ())
755
756 , fPlanarConfiguration (tcPlanarConfiguration, pcInterleaved)
757
758 , fCompression (tcCompression, (uint16) info.fCompression)
759 , fPredictor (tcPredictor , (uint16) info.fPredictor )
760
761 , fExtraSamples (tcExtraSamples,
762 fExtraSamplesData,
763 info.fExtraSamplesCount)
764
765 , fSampleFormat (tcSampleFormat,
766 fSampleFormatData,
767 info.fSamplesPerPixel)
768
769 , fRowInterleaveFactor (tcRowInterleaveFactor,
770 (uint16) info.fRowInterleaveFactor)
771
772 , fSubTileBlockSize (tcSubTileBlockSize,
773 fSubTileBlockSizeData,
774 2)
775
776 {
777
778 uint32 j;
779
780 for (j = 0; j < info.fSamplesPerPixel; j++)
781 {
782
783 fBitsPerSampleData [j] = (uint16) info.fBitsPerSample [0];
784
785 }
786
787 directory.Add (&fNewSubFileType);
788
789 directory.Add (&fImageWidth);
790 directory.Add (&fImageLength);
791
792 directory.Add (&fPhotoInterpretation);
793
794 directory.Add (&fSamplesPerPixel);
795
796 directory.Add (&fBitsPerSample);
797
798 if (info.fBitsPerSample [0] != 8 &&
799 info.fBitsPerSample [0] != 16 &&
800 info.fBitsPerSample [0] != 32)
801 {
802
803 directory.Add (&fFillOrder);
804
805 }
806
807 if (!fStrips)
808 {
809
810 directory.Add (&fTileWidth);
811
812 }
813
814 directory.Add (&fTileLength);
815
816 directory.Add (&fTileOffsets);
817 directory.Add (&fTileByteCounts);
818
819 directory.Add (&fPlanarConfiguration);
820
821 directory.Add (&fCompression);
822
823 if (info.fPredictor != cpNullPredictor)
824 {
825
826 directory.Add (&fPredictor);
827
828 }
829
830 if (info.fExtraSamplesCount != 0)
831 {
832
833 for (j = 0; j < info.fExtraSamplesCount; j++)
834 {
835 fExtraSamplesData [j] = (uint16) info.fExtraSamples [j];
836 }
837
838 directory.Add (&fExtraSamples);
839
840 }
841
842 if (info.fSampleFormat [0] != sfUnsignedInteger)
843 {
844
845 for (j = 0; j < info.fSamplesPerPixel; j++)
846 {
847 fSampleFormatData [j] = (uint16) info.fSampleFormat [j];
848 }
849
850 directory.Add (&fSampleFormat);
851
852 }
853
854 if (info.fRowInterleaveFactor != 1)
855 {
856
857 directory.Add (&fRowInterleaveFactor);
858
859 }
860
861 if (info.fSubTileBlockRows != 1 ||
862 info.fSubTileBlockCols != 1)
863 {
864
865 fSubTileBlockSizeData [0] = (uint16) info.fSubTileBlockRows;
866 fSubTileBlockSizeData [1] = (uint16) info.fSubTileBlockCols;
867
868 directory.Add (&fSubTileBlockSize);
869
870 }
871
872 }
873
874/******************************************************************************/
875
876exif_tag_set::exif_tag_set (dng_tiff_directory &directory,
877 const dng_exif &exif,
878 bool makerNoteSafe,
879 const void *makerNoteData,
880 uint32 makerNoteLength,
881 bool insideDNG)
882
883 : fExifIFD ()
884 , fGPSIFD ()
885
886 , fExifLink (tcExifIFD, 0)
887 , fGPSLink (tcGPSInfo, 0)
888
889 , fAddedExifLink (false)
890 , fAddedGPSLink (false)
891
892 , fExifVersion (tcExifVersion, ttUndefined, 4, fExifVersionData)
893
894 , fExposureTime (tcExposureTime , exif.fExposureTime )
895 , fShutterSpeedValue (tcShutterSpeedValue, exif.fShutterSpeedValue)
896
897 , fFNumber (tcFNumber , exif.fFNumber )
898 , fApertureValue (tcApertureValue, exif.fApertureValue)
899
900 , fBrightnessValue (tcBrightnessValue, exif.fBrightnessValue)
901
902 , fExposureBiasValue (tcExposureBiasValue, exif.fExposureBiasValue)
903
904 , fMaxApertureValue (tcMaxApertureValue , exif.fMaxApertureValue)
905
906 , fSubjectDistance (tcSubjectDistance, exif.fSubjectDistance)
907
908 , fFocalLength (tcFocalLength, exif.fFocalLength)
909
910 // Special case: the EXIF 2.2 standard represents ISO speed ratings with 2 bytes,
911 // which cannot hold ISO speed ratings above 65535 (e.g., 102400). In these
912 // cases, we write the maximum representable ISO speed rating value in the EXIF
913 // tag, i.e., 65535.
914
915 , fISOSpeedRatings (tcISOSpeedRatings,
916 (uint16) Min_uint32 (65535,
917 exif.fISOSpeedRatings [0]))
918
919 , fSensitivityType (tcSensitivityType, (uint16) exif.fSensitivityType)
920
921 , fStandardOutputSensitivity (tcStandardOutputSensitivity, exif.fStandardOutputSensitivity)
922
923 , fRecommendedExposureIndex (tcRecommendedExposureIndex, exif.fRecommendedExposureIndex)
924
925 , fISOSpeed (tcISOSpeed, exif.fISOSpeed)
926
927 , fISOSpeedLatitudeyyy (tcISOSpeedLatitudeyyy, exif.fISOSpeedLatitudeyyy)
928
929 , fISOSpeedLatitudezzz (tcISOSpeedLatitudezzz, exif.fISOSpeedLatitudezzz)
930
931 , fFlash (tcFlash, (uint16) exif.fFlash)
932
933 , fExposureProgram (tcExposureProgram, (uint16) exif.fExposureProgram)
934
935 , fMeteringMode (tcMeteringMode, (uint16) exif.fMeteringMode)
936
937 , fLightSource (tcLightSource, (uint16) exif.fLightSource)
938
939 , fSensingMethod (tcSensingMethodExif, (uint16) exif.fSensingMethod)
940
941 , fFocalLength35mm (tcFocalLengthIn35mmFilm, (uint16) exif.fFocalLengthIn35mmFilm)
942
943 , fFileSourceData ((uint8) exif.fFileSource)
944 , fFileSource (tcFileSource, ttUndefined, 1, &fFileSourceData)
945
946 , fSceneTypeData ((uint8) exif.fSceneType)
947 , fSceneType (tcSceneType, ttUndefined, 1, &fSceneTypeData)
948
949 , fCFAPattern (tcCFAPatternExif,
950 exif.fCFARepeatPatternRows,
951 exif.fCFARepeatPatternCols,
952 &exif.fCFAPattern [0] [0])
953
954 , fCustomRendered (tcCustomRendered , (uint16) exif.fCustomRendered )
955 , fExposureMode (tcExposureMode , (uint16) exif.fExposureMode )
956 , fWhiteBalance (tcWhiteBalance , (uint16) exif.fWhiteBalance )
957 , fSceneCaptureType (tcSceneCaptureType , (uint16) exif.fSceneCaptureType )
958 , fGainControl (tcGainControl , (uint16) exif.fGainControl )
959 , fContrast (tcContrast , (uint16) exif.fContrast )
960 , fSaturation (tcSaturation , (uint16) exif.fSaturation )
961 , fSharpness (tcSharpness , (uint16) exif.fSharpness )
962 , fSubjectDistanceRange (tcSubjectDistanceRange, (uint16) exif.fSubjectDistanceRange)
963
964 , fDigitalZoomRatio (tcDigitalZoomRatio, exif.fDigitalZoomRatio)
965
966 , fExposureIndex (tcExposureIndexExif, exif.fExposureIndex)
967
968 , fImageNumber (tcImageNumber, exif.fImageNumber)
969
970 , fSelfTimerMode (tcSelfTimerMode, (uint16) exif.fSelfTimerMode)
971
972 , fBatteryLevelA (tcBatteryLevel, exif.fBatteryLevelA)
973 , fBatteryLevelR (tcBatteryLevel, exif.fBatteryLevelR)
974
975 , fFocalPlaneXResolution (tcFocalPlaneXResolutionExif, exif.fFocalPlaneXResolution)
976 , fFocalPlaneYResolution (tcFocalPlaneYResolutionExif, exif.fFocalPlaneYResolution)
977
978 , fFocalPlaneResolutionUnit (tcFocalPlaneResolutionUnitExif, (uint16) exif.fFocalPlaneResolutionUnit)
979
980 , fSubjectArea (tcSubjectArea, fSubjectAreaData, exif.fSubjectAreaCount)
981
982 , fLensInfo (tcLensInfo, fLensInfoData, 4)
983
984 , fDateTime (tcDateTime , exif.fDateTime .DateTime ())
985 , fDateTimeOriginal (tcDateTimeOriginal , exif.fDateTimeOriginal .DateTime ())
986 , fDateTimeDigitized (tcDateTimeDigitized, exif.fDateTimeDigitized.DateTime ())
987
988 , fSubsecTime (tcSubsecTime, exif.fDateTime .Subseconds ())
989 , fSubsecTimeOriginal (tcSubsecTimeOriginal, exif.fDateTimeOriginal .Subseconds ())
990 , fSubsecTimeDigitized (tcSubsecTimeDigitized, exif.fDateTimeDigitized.Subseconds ())
991
992 , fMake (tcMake, exif.fMake)
993
994 , fModel (tcModel, exif.fModel)
995
996 , fArtist (tcArtist, exif.fArtist)
997
998 , fSoftware (tcSoftware, exif.fSoftware)
999
1000 , fCopyright (tcCopyright, exif.fCopyright)
1001
1002 , fMakerNoteSafety (tcMakerNoteSafety, makerNoteSafe ? 1 : 0)
1003
1004 , fMakerNote (tcMakerNote, ttUndefined, makerNoteLength, makerNoteData)
1005
1006 , fImageDescription (tcImageDescription, exif.fImageDescription)
1007
1008 , fSerialNumber (tcCameraSerialNumber, exif.fCameraSerialNumber)
1009
1010 , fUserComment (tcUserComment, exif.fUserComment)
1011
1012 , fImageUniqueID (tcImageUniqueID, ttAscii, 33, fImageUniqueIDData)
1013
1014 // EXIF 2.3 tags.
1015
1016 , fCameraOwnerName (tcCameraOwnerNameExif, exif.fOwnerName )
1017 , fBodySerialNumber (tcCameraSerialNumberExif, exif.fCameraSerialNumber)
1018 , fLensSpecification (tcLensSpecificationExif, fLensInfoData, 4 )
1019 , fLensMake (tcLensMakeExif, exif.fLensMake )
1020 , fLensModel (tcLensModelExif, exif.fLensName )
1021 , fLensSerialNumber (tcLensSerialNumberExif, exif.fLensSerialNumber )
1022
1023 , fGPSVersionID (tcGPSVersionID, fGPSVersionData, 4)
1024
1025 , fGPSLatitudeRef (tcGPSLatitudeRef, exif.fGPSLatitudeRef)
1026 , fGPSLatitude (tcGPSLatitude, exif.fGPSLatitude, 3)
1027
1028 , fGPSLongitudeRef (tcGPSLongitudeRef, exif.fGPSLongitudeRef)
1029 , fGPSLongitude (tcGPSLongitude, exif.fGPSLongitude, 3)
1030
1031 , fGPSAltitudeRef (tcGPSAltitudeRef, (uint8) exif.fGPSAltitudeRef)
1032 , fGPSAltitude (tcGPSAltitude, exif.fGPSAltitude )
1033
1034 , fGPSTimeStamp (tcGPSTimeStamp, exif.fGPSTimeStamp, 3)
1035
1036 , fGPSSatellites (tcGPSSatellites , exif.fGPSSatellites )
1037 , fGPSStatus (tcGPSStatus , exif.fGPSStatus )
1038 , fGPSMeasureMode (tcGPSMeasureMode, exif.fGPSMeasureMode)
1039
1040 , fGPSDOP (tcGPSDOP, exif.fGPSDOP)
1041
1042 , fGPSSpeedRef (tcGPSSpeedRef, exif.fGPSSpeedRef)
1043 , fGPSSpeed (tcGPSSpeed , exif.fGPSSpeed )
1044
1045 , fGPSTrackRef (tcGPSTrackRef, exif.fGPSTrackRef)
1046 , fGPSTrack (tcGPSTrack , exif.fGPSTrack )
1047
1048 , fGPSImgDirectionRef (tcGPSImgDirectionRef, exif.fGPSImgDirectionRef)
1049 , fGPSImgDirection (tcGPSImgDirection , exif.fGPSImgDirection )
1050
1051 , fGPSMapDatum (tcGPSMapDatum, exif.fGPSMapDatum)
1052
1053 , fGPSDestLatitudeRef (tcGPSDestLatitudeRef, exif.fGPSDestLatitudeRef)
1054 , fGPSDestLatitude (tcGPSDestLatitude, exif.fGPSDestLatitude, 3)
1055
1056 , fGPSDestLongitudeRef (tcGPSDestLongitudeRef, exif.fGPSDestLongitudeRef)
1057 , fGPSDestLongitude (tcGPSDestLongitude, exif.fGPSDestLongitude, 3)
1058
1059 , fGPSDestBearingRef (tcGPSDestBearingRef, exif.fGPSDestBearingRef)
1060 , fGPSDestBearing (tcGPSDestBearing , exif.fGPSDestBearing )
1061
1062 , fGPSDestDistanceRef (tcGPSDestDistanceRef, exif.fGPSDestDistanceRef)
1063 , fGPSDestDistance (tcGPSDestDistance , exif.fGPSDestDistance )
1064
1065 , fGPSProcessingMethod (tcGPSProcessingMethod, exif.fGPSProcessingMethod)
1066 , fGPSAreaInformation (tcGPSAreaInformation , exif.fGPSAreaInformation )
1067
1068 , fGPSDateStamp (tcGPSDateStamp, exif.fGPSDateStamp)
1069
1070 , fGPSDifferential (tcGPSDifferential, (uint16) exif.fGPSDifferential)
1071
1072 , fGPSHPositioningError (tcGPSHPositioningError, exif.fGPSHPositioningError)
1073
1074 {
1075
1076 if (exif.fExifVersion)
1077 {
1078
1079 fExifVersionData [0] = (uint8) (exif.fExifVersion >> 24);
1080 fExifVersionData [1] = (uint8) (exif.fExifVersion >> 16);
1081 fExifVersionData [2] = (uint8) (exif.fExifVersion >> 8);
1082 fExifVersionData [3] = (uint8) (exif.fExifVersion );
1083
1084 fExifIFD.Add (&fExifVersion);
1085
1086 }
1087
1088 if (exif.fExposureTime.IsValid ())
1089 {
1090 fExifIFD.Add (&fExposureTime);
1091 }
1092
1093 if (exif.fShutterSpeedValue.IsValid ())
1094 {
1095 fExifIFD.Add (&fShutterSpeedValue);
1096 }
1097
1098 if (exif.fFNumber.IsValid ())
1099 {
1100 fExifIFD.Add (&fFNumber);
1101 }
1102
1103 if (exif.fApertureValue.IsValid ())
1104 {
1105 fExifIFD.Add (&fApertureValue);
1106 }
1107
1108 if (exif.fBrightnessValue.IsValid ())
1109 {
1110 fExifIFD.Add (&fBrightnessValue);
1111 }
1112
1113 if (exif.fExposureBiasValue.IsValid ())
1114 {
1115 fExifIFD.Add (&fExposureBiasValue);
1116 }
1117
1118 if (exif.fMaxApertureValue.IsValid ())
1119 {
1120 fExifIFD.Add (&fMaxApertureValue);
1121 }
1122
1123 if (exif.fSubjectDistance.IsValid ())
1124 {
1125 fExifIFD.Add (&fSubjectDistance);
1126 }
1127
1128 if (exif.fFocalLength.IsValid ())
1129 {
1130 fExifIFD.Add (&fFocalLength);
1131 }
1132
1133 if (exif.fISOSpeedRatings [0] != 0)
1134 {
1135 fExifIFD.Add (&fISOSpeedRatings);
1136 }
1137
1138 if (exif.fFlash <= 0x0FFFF)
1139 {
1140 fExifIFD.Add (&fFlash);
1141 }
1142
1143 if (exif.fExposureProgram <= 0x0FFFF)
1144 {
1145 fExifIFD.Add (&fExposureProgram);
1146 }
1147
1148 if (exif.fMeteringMode <= 0x0FFFF)
1149 {
1150 fExifIFD.Add (&fMeteringMode);
1151 }
1152
1153 if (exif.fLightSource <= 0x0FFFF)
1154 {
1155 fExifIFD.Add (&fLightSource);
1156 }
1157
1158 if (exif.fSensingMethod <= 0x0FFFF)
1159 {
1160 fExifIFD.Add (&fSensingMethod);
1161 }
1162
1163 if (exif.fFocalLengthIn35mmFilm != 0)
1164 {
1165 fExifIFD.Add (&fFocalLength35mm);
1166 }
1167
1168 if (exif.fFileSource <= 0x0FF)
1169 {
1170 fExifIFD.Add (&fFileSource);
1171 }
1172
1173 if (exif.fSceneType <= 0x0FF)
1174 {
1175 fExifIFD.Add (&fSceneType);
1176 }
1177
1178 if (exif.fCFARepeatPatternRows &&
1179 exif.fCFARepeatPatternCols)
1180 {
1181 fExifIFD.Add (&fCFAPattern);
1182 }
1183
1184 if (exif.fCustomRendered <= 0x0FFFF)
1185 {
1186 fExifIFD.Add (&fCustomRendered);
1187 }
1188
1189 if (exif.fExposureMode <= 0x0FFFF)
1190 {
1191 fExifIFD.Add (&fExposureMode);
1192 }
1193
1194 if (exif.fWhiteBalance <= 0x0FFFF)
1195 {
1196 fExifIFD.Add (&fWhiteBalance);
1197 }
1198
1199 if (exif.fSceneCaptureType <= 0x0FFFF)
1200 {
1201 fExifIFD.Add (&fSceneCaptureType);
1202 }
1203
1204 if (exif.fGainControl <= 0x0FFFF)
1205 {
1206 fExifIFD.Add (&fGainControl);
1207 }
1208
1209 if (exif.fContrast <= 0x0FFFF)
1210 {
1211 fExifIFD.Add (&fContrast);
1212 }
1213
1214 if (exif.fSaturation <= 0x0FFFF)
1215 {
1216 fExifIFD.Add (&fSaturation);
1217 }
1218
1219 if (exif.fSharpness <= 0x0FFFF)
1220 {
1221 fExifIFD.Add (&fSharpness);
1222 }
1223
1224 if (exif.fSubjectDistanceRange <= 0x0FFFF)
1225 {
1226 fExifIFD.Add (&fSubjectDistanceRange);
1227 }
1228
1229 if (exif.fDigitalZoomRatio.IsValid ())
1230 {
1231 fExifIFD.Add (&fDigitalZoomRatio);
1232 }
1233
1234 if (exif.fExposureIndex.IsValid ())
1235 {
1236 fExifIFD.Add (&fExposureIndex);
1237 }
1238
1239 if (insideDNG) // TIFF-EP only tags
1240 {
1241
1242 if (exif.fImageNumber != 0xFFFFFFFF)
1243 {
1244 directory.Add (&fImageNumber);
1245 }
1246
1247 if (exif.fSelfTimerMode <= 0x0FFFF)
1248 {
1249 directory.Add (&fSelfTimerMode);
1250 }
1251
1252 if (exif.fBatteryLevelA.NotEmpty ())
1253 {
1254 directory.Add (&fBatteryLevelA);
1255 }
1256
1257 else if (exif.fBatteryLevelR.IsValid ())
1258 {
1259 directory.Add (&fBatteryLevelR);
1260 }
1261
1262 }
1263
1264 if (exif.fFocalPlaneXResolution.IsValid ())
1265 {
1266 fExifIFD.Add (&fFocalPlaneXResolution);
1267 }
1268
1269 if (exif.fFocalPlaneYResolution.IsValid ())
1270 {
1271 fExifIFD.Add (&fFocalPlaneYResolution);
1272 }
1273
1274 if (exif.fFocalPlaneResolutionUnit <= 0x0FFFF)
1275 {
1276 fExifIFD.Add (&fFocalPlaneResolutionUnit);
1277 }
1278
1279 if (exif.fSubjectAreaCount)
1280 {
1281
1282 fSubjectAreaData [0] = (uint16) exif.fSubjectArea [0];
1283 fSubjectAreaData [1] = (uint16) exif.fSubjectArea [1];
1284 fSubjectAreaData [2] = (uint16) exif.fSubjectArea [2];
1285 fSubjectAreaData [3] = (uint16) exif.fSubjectArea [3];
1286
1287 fExifIFD.Add (&fSubjectArea);
1288
1289 }
1290
1291 if (exif.fLensInfo [0].IsValid () &&
1292 exif.fLensInfo [1].IsValid ())
1293 {
1294
1295 fLensInfoData [0] = exif.fLensInfo [0];
1296 fLensInfoData [1] = exif.fLensInfo [1];
1297 fLensInfoData [2] = exif.fLensInfo [2];
1298 fLensInfoData [3] = exif.fLensInfo [3];
1299
1300 if (insideDNG)
1301 {
1302 directory.Add (&fLensInfo);
1303 }
1304
1305 }
1306
1307 if (exif.fDateTime.IsValid ())
1308 {
1309
1310 directory.Add (&fDateTime);
1311
1312 if (exif.fDateTime.Subseconds ().NotEmpty ())
1313 {
1314 fExifIFD.Add (&fSubsecTime);
1315 }
1316
1317 }
1318
1319 if (exif.fDateTimeOriginal.IsValid ())
1320 {
1321
1322 fExifIFD.Add (&fDateTimeOriginal);
1323
1324 if (exif.fDateTimeOriginal.Subseconds ().NotEmpty ())
1325 {
1326 fExifIFD.Add (&fSubsecTimeOriginal);
1327 }
1328
1329 }
1330
1331 if (exif.fDateTimeDigitized.IsValid ())
1332 {
1333
1334 fExifIFD.Add (&fDateTimeDigitized);
1335
1336 if (exif.fDateTimeDigitized.Subseconds ().NotEmpty ())
1337 {
1338 fExifIFD.Add (&fSubsecTimeDigitized);
1339 }
1340
1341 }
1342
1343 if (exif.fMake.NotEmpty ())
1344 {
1345 directory.Add (&fMake);
1346 }
1347
1348 if (exif.fModel.NotEmpty ())
1349 {
1350 directory.Add (&fModel);
1351 }
1352
1353 if (exif.fArtist.NotEmpty ())
1354 {
1355 directory.Add (&fArtist);
1356 }
1357
1358 if (exif.fSoftware.NotEmpty ())
1359 {
1360 directory.Add (&fSoftware);
1361 }
1362
1363 if (exif.fCopyright.NotEmpty ())
1364 {
1365 directory.Add (&fCopyright);
1366 }
1367
1368 if (exif.fImageDescription.NotEmpty ())
1369 {
1370 directory.Add (&fImageDescription);
1371 }
1372
1373 if (exif.fCameraSerialNumber.NotEmpty () && insideDNG)
1374 {
1375 directory.Add (&fSerialNumber);
1376 }
1377
1378 if (makerNoteSafe && makerNoteData)
1379 {
1380
1381 directory.Add (&fMakerNoteSafety);
1382
1383 fExifIFD.Add (&fMakerNote);
1384
1385 }
1386
1387 if (exif.fUserComment.NotEmpty ())
1388 {
1389 fExifIFD.Add (&fUserComment);
1390 }
1391
1392 if (exif.fImageUniqueID.IsValid ())
1393 {
1394
1395 for (uint32 j = 0; j < 16; j++)
1396 {
1397
1398 sprintf (fImageUniqueIDData + j * 2,
1399 "%02X",
1400 (unsigned) exif.fImageUniqueID.data [j]);
1401
1402 }
1403
1404 fExifIFD.Add (&fImageUniqueID);
1405
1406 }
1407
1408 if (exif.AtLeastVersion0230 ())
1409 {
1410
1411 if (exif.fSensitivityType != 0)
1412 {
1413
1414 fExifIFD.Add (&fSensitivityType);
1415
1416 }
1417
1418 // Sensitivity tags. Do not write these extra tags unless the SensitivityType
1419 // and PhotographicSensitivity (i.e., ISOSpeedRatings) values are valid.
1420
1421 if (exif.fSensitivityType != 0 &&
1422 exif.fISOSpeedRatings [0] != 0)
1423 {
1424
1425 // Standard Output Sensitivity (SOS).
1426
1427 if (exif.fStandardOutputSensitivity != 0)
1428 {
1429 fExifIFD.Add (&fStandardOutputSensitivity);
1430 }
1431
1432 // Recommended Exposure Index (REI).
1433
1434 if (exif.fRecommendedExposureIndex != 0)
1435 {
1436 fExifIFD.Add (&fRecommendedExposureIndex);
1437 }
1438
1439 // ISO Speed.
1440
1441 if (exif.fISOSpeed != 0)
1442 {
1443
1444 fExifIFD.Add (&fISOSpeed);
1445
1446 if (exif.fISOSpeedLatitudeyyy != 0 &&
1447 exif.fISOSpeedLatitudezzz != 0)
1448 {
1449
1450 fExifIFD.Add (&fISOSpeedLatitudeyyy);
1451 fExifIFD.Add (&fISOSpeedLatitudezzz);
1452
1453 }
1454
1455 }
1456
1457 }
1458
1459 if (exif.fOwnerName.NotEmpty ())
1460 {
1461 fExifIFD.Add (&fCameraOwnerName);
1462 }
1463
1464 if (exif.fCameraSerialNumber.NotEmpty ())
1465 {
1466 fExifIFD.Add (&fBodySerialNumber);
1467 }
1468
1469 if (exif.fLensInfo [0].IsValid () &&
1470 exif.fLensInfo [1].IsValid ())
1471 {
1472 fExifIFD.Add (&fLensSpecification);
1473 }
1474
1475 if (exif.fLensMake.NotEmpty ())
1476 {
1477 fExifIFD.Add (&fLensMake);
1478 }
1479
1480 if (exif.fLensName.NotEmpty ())
1481 {
1482 fExifIFD.Add (&fLensModel);
1483 }
1484
1485 if (exif.fLensSerialNumber.NotEmpty ())
1486 {
1487 fExifIFD.Add (&fLensSerialNumber);
1488 }
1489
1490 }
1491
1492 if (exif.fGPSVersionID)
1493 {
1494
1495 fGPSVersionData [0] = (uint8) (exif.fGPSVersionID >> 24);
1496 fGPSVersionData [1] = (uint8) (exif.fGPSVersionID >> 16);
1497 fGPSVersionData [2] = (uint8) (exif.fGPSVersionID >> 8);
1498 fGPSVersionData [3] = (uint8) (exif.fGPSVersionID );
1499
1500 fGPSIFD.Add (&fGPSVersionID);
1501
1502 }
1503
1504 if (exif.fGPSLatitudeRef.NotEmpty () &&
1505 exif.fGPSLatitude [0].IsValid ())
1506 {
1507 fGPSIFD.Add (&fGPSLatitudeRef);
1508 fGPSIFD.Add (&fGPSLatitude );
1509 }
1510
1511 if (exif.fGPSLongitudeRef.NotEmpty () &&
1512 exif.fGPSLongitude [0].IsValid ())
1513 {
1514 fGPSIFD.Add (&fGPSLongitudeRef);
1515 fGPSIFD.Add (&fGPSLongitude );
1516 }
1517
1518 if (exif.fGPSAltitudeRef <= 0x0FF)
1519 {
1520 fGPSIFD.Add (&fGPSAltitudeRef);
1521 }
1522
1523 if (exif.fGPSAltitude.IsValid ())
1524 {
1525 fGPSIFD.Add (&fGPSAltitude);
1526 }
1527
1528 if (exif.fGPSTimeStamp [0].IsValid ())
1529 {
1530 fGPSIFD.Add (&fGPSTimeStamp);
1531 }
1532
1533 if (exif.fGPSSatellites.NotEmpty ())
1534 {
1535 fGPSIFD.Add (&fGPSSatellites);
1536 }
1537
1538 if (exif.fGPSStatus.NotEmpty ())
1539 {
1540 fGPSIFD.Add (&fGPSStatus);
1541 }
1542
1543 if (exif.fGPSMeasureMode.NotEmpty ())
1544 {
1545 fGPSIFD.Add (&fGPSMeasureMode);
1546 }
1547
1548 if (exif.fGPSDOP.IsValid ())
1549 {
1550 fGPSIFD.Add (&fGPSDOP);
1551 }
1552
1553 if (exif.fGPSSpeedRef.NotEmpty ())
1554 {
1555 fGPSIFD.Add (&fGPSSpeedRef);
1556 }
1557
1558 if (exif.fGPSSpeed.IsValid ())
1559 {
1560 fGPSIFD.Add (&fGPSSpeed);
1561 }
1562
1563 if (exif.fGPSTrackRef.NotEmpty ())
1564 {
1565 fGPSIFD.Add (&fGPSTrackRef);
1566 }
1567
1568 if (exif.fGPSTrack.IsValid ())
1569 {
1570 fGPSIFD.Add (&fGPSTrack);
1571 }
1572
1573 if (exif.fGPSImgDirectionRef.NotEmpty ())
1574 {
1575 fGPSIFD.Add (&fGPSImgDirectionRef);
1576 }
1577
1578 if (exif.fGPSImgDirection.IsValid ())
1579 {
1580 fGPSIFD.Add (&fGPSImgDirection);
1581 }
1582
1583 if (exif.fGPSMapDatum.NotEmpty ())
1584 {
1585 fGPSIFD.Add (&fGPSMapDatum);
1586 }
1587
1588 if (exif.fGPSDestLatitudeRef.NotEmpty () &&
1589 exif.fGPSDestLatitude [0].IsValid ())
1590 {
1591 fGPSIFD.Add (&fGPSDestLatitudeRef);
1592 fGPSIFD.Add (&fGPSDestLatitude );
1593 }
1594
1595 if (exif.fGPSDestLongitudeRef.NotEmpty () &&
1596 exif.fGPSDestLongitude [0].IsValid ())
1597 {
1598 fGPSIFD.Add (&fGPSDestLongitudeRef);
1599 fGPSIFD.Add (&fGPSDestLongitude );
1600 }
1601
1602 if (exif.fGPSDestBearingRef.NotEmpty ())
1603 {
1604 fGPSIFD.Add (&fGPSDestBearingRef);
1605 }
1606
1607 if (exif.fGPSDestBearing.IsValid ())
1608 {
1609 fGPSIFD.Add (&fGPSDestBearing);
1610 }
1611
1612 if (exif.fGPSDestDistanceRef.NotEmpty ())
1613 {
1614 fGPSIFD.Add (&fGPSDestDistanceRef);
1615 }
1616
1617 if (exif.fGPSDestDistance.IsValid ())
1618 {
1619 fGPSIFD.Add (&fGPSDestDistance);
1620 }
1621
1622 if (exif.fGPSProcessingMethod.NotEmpty ())
1623 {
1624 fGPSIFD.Add (&fGPSProcessingMethod);
1625 }
1626
1627 if (exif.fGPSAreaInformation.NotEmpty ())
1628 {
1629 fGPSIFD.Add (&fGPSAreaInformation);
1630 }
1631
1632 if (exif.fGPSDateStamp.NotEmpty ())
1633 {
1634 fGPSIFD.Add (&fGPSDateStamp);
1635 }
1636
1637 if (exif.fGPSDifferential <= 0x0FFFF)
1638 {
1639 fGPSIFD.Add (&fGPSDifferential);
1640 }
1641
1642 if (exif.AtLeastVersion0230 ())
1643 {
1644
1645 if (exif.fGPSHPositioningError.IsValid ())
1646 {
1647 fGPSIFD.Add (&fGPSHPositioningError);
1648 }
1649
1650 }
1651
1652 AddLinks (directory);
1653
1654 }
1655
1656/******************************************************************************/
1657
1658void exif_tag_set::AddLinks (dng_tiff_directory &directory)
1659 {
1660
1661 if (fExifIFD.Size () != 0 && !fAddedExifLink)
1662 {
1663
1664 directory.Add (&fExifLink);
1665
1666 fAddedExifLink = true;
1667
1668 }
1669
1670 if (fGPSIFD.Size () != 0 && !fAddedGPSLink)
1671 {
1672
1673 directory.Add (&fGPSLink);
1674
1675 fAddedGPSLink = true;
1676
1677 }
1678
1679 }
1680
1681/******************************************************************************/
1682
1683class range_tag_set
1684 {
1685
1686 private:
1687
1688 uint32 fActiveAreaData [4];
1689
1690 tag_uint32_ptr fActiveArea;
1691
1692 uint32 fMaskedAreaData [kMaxMaskedAreas * 4];
1693
1694 tag_uint32_ptr fMaskedAreas;
1695
1696 tag_uint16_ptr fLinearizationTable;
1697
1698 uint16 fBlackLevelRepeatDimData [2];
1699
1700 tag_uint16_ptr fBlackLevelRepeatDim;
1701
1702 dng_urational fBlackLevelData [kMaxBlackPattern *
1703 kMaxBlackPattern *
1704 kMaxSamplesPerPixel];
1705
1706 tag_urational_ptr fBlackLevel;
1707
1708 dng_memory_data fBlackLevelDeltaHData;
1709 dng_memory_data fBlackLevelDeltaVData;
1710
1711 tag_srational_ptr fBlackLevelDeltaH;
1712 tag_srational_ptr fBlackLevelDeltaV;
1713
1714 uint16 fWhiteLevelData16 [kMaxSamplesPerPixel];
1715 uint32 fWhiteLevelData32 [kMaxSamplesPerPixel];
1716
1717 tag_uint16_ptr fWhiteLevel16;
1718 tag_uint32_ptr fWhiteLevel32;
1719
1720 public:
1721
1722 range_tag_set (dng_tiff_directory &directory,
1723 const dng_negative &negative);
1724
1725 };
1726
1727/******************************************************************************/
1728
1729range_tag_set::range_tag_set (dng_tiff_directory &directory,
1730 const dng_negative &negative)
1731
1732 : fActiveArea (tcActiveArea,
1733 fActiveAreaData,
1734 4)
1735
1736 , fMaskedAreas (tcMaskedAreas,
1737 fMaskedAreaData,
1738 0)
1739
1740 , fLinearizationTable (tcLinearizationTable,
1741 NULL,
1742 0)
1743
1744 , fBlackLevelRepeatDim (tcBlackLevelRepeatDim,
1745 fBlackLevelRepeatDimData,
1746 2)
1747
1748 , fBlackLevel (tcBlackLevel,
1749 fBlackLevelData)
1750
1751 , fBlackLevelDeltaHData ()
1752 , fBlackLevelDeltaVData ()
1753
1754 , fBlackLevelDeltaH (tcBlackLevelDeltaH)
1755 , fBlackLevelDeltaV (tcBlackLevelDeltaV)
1756
1757 , fWhiteLevel16 (tcWhiteLevel,
1758 fWhiteLevelData16)
1759
1760 , fWhiteLevel32 (tcWhiteLevel,
1761 fWhiteLevelData32)
1762
1763 {
1764
1765 const dng_image &rawImage (negative.RawImage ());
1766
1767 const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo ();
1768
1769 if (rangeInfo)
1770 {
1771
1772 // ActiveArea:
1773
1774 {
1775
1776 const dng_rect &r = rangeInfo->fActiveArea;
1777
1778 if (r.NotEmpty ())
1779 {
1780
1781 fActiveAreaData [0] = r.t;
1782 fActiveAreaData [1] = r.l;
1783 fActiveAreaData [2] = r.b;
1784 fActiveAreaData [3] = r.r;
1785
1786 directory.Add (&fActiveArea);
1787
1788 }
1789
1790 }
1791
1792 // MaskedAreas:
1793
1794 if (rangeInfo->fMaskedAreaCount)
1795 {
1796
1797 fMaskedAreas.SetCount (rangeInfo->fMaskedAreaCount * 4);
1798
1799 for (uint32 index = 0; index < rangeInfo->fMaskedAreaCount; index++)
1800 {
1801
1802 const dng_rect &r = rangeInfo->fMaskedArea [index];
1803
1804 fMaskedAreaData [index * 4 + 0] = r.t;
1805 fMaskedAreaData [index * 4 + 1] = r.l;
1806 fMaskedAreaData [index * 4 + 2] = r.b;
1807 fMaskedAreaData [index * 4 + 3] = r.r;
1808
1809 }
1810
1811 directory.Add (&fMaskedAreas);
1812
1813 }
1814
1815 // LinearizationTable:
1816
1817 if (rangeInfo->fLinearizationTable.Get ())
1818 {
1819
1820 fLinearizationTable.SetData (rangeInfo->fLinearizationTable->Buffer_uint16 () );
1821 fLinearizationTable.SetCount (rangeInfo->fLinearizationTable->LogicalSize () >> 1);
1822
1823 directory.Add (&fLinearizationTable);
1824
1825 }
1826
1827 // BlackLevelRepeatDim:
1828
1829 {
1830
1831 fBlackLevelRepeatDimData [0] = (uint16) rangeInfo->fBlackLevelRepeatRows;
1832 fBlackLevelRepeatDimData [1] = (uint16) rangeInfo->fBlackLevelRepeatCols;
1833
1834 directory.Add (&fBlackLevelRepeatDim);
1835
1836 }
1837
1838 // BlackLevel:
1839
1840 {
1841
1842 uint32 index = 0;
1843
1844 for (uint16 v = 0; v < rangeInfo->fBlackLevelRepeatRows; v++)
1845 {
1846
1847 for (uint32 h = 0; h < rangeInfo->fBlackLevelRepeatCols; h++)
1848 {
1849
1850 for (uint32 c = 0; c < rawImage.Planes (); c++)
1851 {
1852
1853 fBlackLevelData [index++] = rangeInfo->BlackLevel (v, h, c);
1854
1855 }
1856
1857 }
1858
1859 }
1860
1861 fBlackLevel.SetCount (rangeInfo->fBlackLevelRepeatRows *
1862 rangeInfo->fBlackLevelRepeatCols * rawImage.Planes ());
1863
1864 directory.Add (&fBlackLevel);
1865
1866 }
1867
1868 // BlackLevelDeltaH:
1869
1870 if (rangeInfo->ColumnBlackCount ())
1871 {
1872
1873 uint32 count = rangeInfo->ColumnBlackCount ();
1874
1875 fBlackLevelDeltaHData.Allocate (count, sizeof (dng_srational));
1876
1877 dng_srational *blacks = (dng_srational *) fBlackLevelDeltaHData.Buffer ();
1878
1879 for (uint32 col = 0; col < count; col++)
1880 {
1881
1882 blacks [col] = rangeInfo->ColumnBlack (col);
1883
1884 }
1885
1886 fBlackLevelDeltaH.SetData (blacks);
1887 fBlackLevelDeltaH.SetCount (count );
1888
1889 directory.Add (&fBlackLevelDeltaH);
1890
1891 }
1892
1893 // BlackLevelDeltaV:
1894
1895 if (rangeInfo->RowBlackCount ())
1896 {
1897
1898 uint32 count = rangeInfo->RowBlackCount ();
1899
1900 fBlackLevelDeltaVData.Allocate (count, sizeof (dng_srational));
1901
1902 dng_srational *blacks = (dng_srational *) fBlackLevelDeltaVData.Buffer ();
1903
1904 for (uint32 row = 0; row < count; row++)
1905 {
1906
1907 blacks [row] = rangeInfo->RowBlack (row);
1908
1909 }
1910
1911 fBlackLevelDeltaV.SetData (blacks);
1912 fBlackLevelDeltaV.SetCount (count );
1913
1914 directory.Add (&fBlackLevelDeltaV);
1915
1916 }
1917
1918 }
1919
1920 // WhiteLevel:
1921
1922 // Only use the 32-bit data type if we must use it since there
1923 // are some lazy (non-Adobe) DNG readers out there.
1924
1925 bool needs32 = false;
1926
1927 fWhiteLevel16.SetCount (rawImage.Planes ());
1928 fWhiteLevel32.SetCount (rawImage.Planes ());
1929
1930 for (uint32 c = 0; c < fWhiteLevel16.Count (); c++)
1931 {
1932
1933 fWhiteLevelData32 [c] = negative.WhiteLevel (c);
1934
1935 if (fWhiteLevelData32 [c] > 0x0FFFF)
1936 {
1937 needs32 = true;
1938 }
1939
1940 fWhiteLevelData16 [c] = (uint16) fWhiteLevelData32 [c];
1941
1942 }
1943
1944 if (needs32)
1945 {
1946 directory.Add (&fWhiteLevel32);
1947 }
1948
1949 else
1950 {
1951 directory.Add (&fWhiteLevel16);
1952 }
1953
1954 }
1955
1956/******************************************************************************/
1957
1958class mosaic_tag_set
1959 {
1960
1961 private:
1962
1963 uint16 fCFARepeatPatternDimData [2];
1964
1965 tag_uint16_ptr fCFARepeatPatternDim;
1966
1967 uint8 fCFAPatternData [kMaxCFAPattern *
1968 kMaxCFAPattern];
1969
1970 tag_uint8_ptr fCFAPattern;
1971
1972 uint8 fCFAPlaneColorData [kMaxColorPlanes];
1973
1974 tag_uint8_ptr fCFAPlaneColor;
1975
1976 tag_uint16 fCFALayout;
1977
1978 tag_uint32 fGreenSplit;
1979
1980 public:
1981
1982 mosaic_tag_set (dng_tiff_directory &directory,
1983 const dng_mosaic_info &info);
1984
1985 };
1986
1987/******************************************************************************/
1988
1989mosaic_tag_set::mosaic_tag_set (dng_tiff_directory &directory,
1990 const dng_mosaic_info &info)
1991
1992 : fCFARepeatPatternDim (tcCFARepeatPatternDim,
1993 fCFARepeatPatternDimData,
1994 2)
1995
1996 , fCFAPattern (tcCFAPattern,
1997 fCFAPatternData)
1998
1999 , fCFAPlaneColor (tcCFAPlaneColor,
2000 fCFAPlaneColorData)
2001
2002 , fCFALayout (tcCFALayout,
2003 (uint16) info.fCFALayout)
2004
2005 , fGreenSplit (tcBayerGreenSplit,
2006 info.fBayerGreenSplit)
2007
2008 {
2009
2010 if (info.IsColorFilterArray ())
2011 {
2012
2013 // CFARepeatPatternDim:
2014
2015 fCFARepeatPatternDimData [0] = (uint16) info.fCFAPatternSize.v;
2016 fCFARepeatPatternDimData [1] = (uint16) info.fCFAPatternSize.h;
2017
2018 directory.Add (&fCFARepeatPatternDim);
2019
2020 // CFAPattern:
2021
2022 fCFAPattern.SetCount (info.fCFAPatternSize.v *
2023 info.fCFAPatternSize.h);
2024
2025 for (int32 r = 0; r < info.fCFAPatternSize.v; r++)
2026 {
2027
2028 for (int32 c = 0; c < info.fCFAPatternSize.h; c++)
2029 {
2030
2031 fCFAPatternData [r * info.fCFAPatternSize.h + c] = info.fCFAPattern [r] [c];
2032
2033 }
2034
2035 }
2036
2037 directory.Add (&fCFAPattern);
2038
2039 // CFAPlaneColor:
2040
2041 fCFAPlaneColor.SetCount (info.fColorPlanes);
2042
2043 for (uint32 j = 0; j < info.fColorPlanes; j++)
2044 {
2045
2046 fCFAPlaneColorData [j] = info.fCFAPlaneColor [j];
2047
2048 }
2049
2050 directory.Add (&fCFAPlaneColor);
2051
2052 // CFALayout:
2053
2054 fCFALayout.Set ((uint16) info.fCFALayout);
2055
2056 directory.Add (&fCFALayout);
2057
2058 // BayerGreenSplit: (only include if the pattern is a Bayer pattern)
2059
2060 if (info.fCFAPatternSize == dng_point (2, 2) &&
2061 info.fColorPlanes == 3)
2062 {
2063
2064 directory.Add (&fGreenSplit);
2065
2066 }
2067
2068 }
2069
2070 }
2071
2072/******************************************************************************/
2073
2074class color_tag_set
2075 {
2076
2077 private:
2078
2079 uint32 fColorChannels;
2080
2081 tag_matrix fCameraCalibration1;
2082 tag_matrix fCameraCalibration2;
2083
2084 tag_string fCameraCalibrationSignature;
2085
2086 tag_string fAsShotProfileName;
2087
2088 dng_urational fAnalogBalanceData [4];
2089
2090 tag_urational_ptr fAnalogBalance;
2091
2092 dng_urational fAsShotNeutralData [4];
2093
2094 tag_urational_ptr fAsShotNeutral;
2095
2096 dng_urational fAsShotWhiteXYData [2];
2097
2098 tag_urational_ptr fAsShotWhiteXY;
2099
2100 tag_urational fLinearResponseLimit;
2101
2102 public:
2103
2104 color_tag_set (dng_tiff_directory &directory,
2105 const dng_negative &negative);
2106
2107 };
2108
2109/******************************************************************************/
2110
2111color_tag_set::color_tag_set (dng_tiff_directory &directory,
2112 const dng_negative &negative)
2113
2114 : fColorChannels (negative.ColorChannels ())
2115
2116 , fCameraCalibration1 (tcCameraCalibration1,
2117 negative.CameraCalibration1 ())
2118
2119 , fCameraCalibration2 (tcCameraCalibration2,
2120 negative.CameraCalibration2 ())
2121
2122 , fCameraCalibrationSignature (tcCameraCalibrationSignature,
2123 negative.CameraCalibrationSignature ())
2124
2125 , fAsShotProfileName (tcAsShotProfileName,
2126 negative.AsShotProfileName ())
2127
2128 , fAnalogBalance (tcAnalogBalance,
2129 fAnalogBalanceData,
2130 fColorChannels)
2131
2132 , fAsShotNeutral (tcAsShotNeutral,
2133 fAsShotNeutralData,
2134 fColorChannels)
2135
2136 , fAsShotWhiteXY (tcAsShotWhiteXY,
2137 fAsShotWhiteXYData,
2138 2)
2139
2140 , fLinearResponseLimit (tcLinearResponseLimit,
2141 negative.LinearResponseLimitR ())
2142
2143 {
2144
2145 if (fColorChannels > 1)
2146 {
2147
2148 uint32 channels2 = fColorChannels * fColorChannels;
2149
2150 if (fCameraCalibration1.Count () == channels2)
2151 {
2152
2153 directory.Add (&fCameraCalibration1);
2154
2155 }
2156
2157 if (fCameraCalibration2.Count () == channels2)
2158 {
2159
2160 directory.Add (&fCameraCalibration2);
2161
2162 }
2163
2164 if (fCameraCalibration1.Count () == channels2 ||
2165 fCameraCalibration2.Count () == channels2)
2166 {
2167
2168 if (negative.CameraCalibrationSignature ().NotEmpty ())
2169 {
2170
2171 directory.Add (&fCameraCalibrationSignature);
2172
2173 }
2174
2175 }
2176
2177 if (negative.AsShotProfileName ().NotEmpty ())
2178 {
2179
2180 directory.Add (&fAsShotProfileName);
2181
2182 }
2183
2184 for (uint32 j = 0; j < fColorChannels; j++)
2185 {
2186
2187 fAnalogBalanceData [j] = negative.AnalogBalanceR (j);
2188
2189 }
2190
2191 directory.Add (&fAnalogBalance);
2192
2193 if (negative.HasCameraNeutral ())
2194 {
2195
2196 for (uint32 k = 0; k < fColorChannels; k++)
2197 {
2198
2199 fAsShotNeutralData [k] = negative.CameraNeutralR (k);
2200
2201 }
2202
2203 directory.Add (&fAsShotNeutral);
2204
2205 }
2206
2207 else if (negative.HasCameraWhiteXY ())
2208 {
2209
2210 negative.GetCameraWhiteXY (fAsShotWhiteXYData [0],
2211 fAsShotWhiteXYData [1]);
2212
2213 directory.Add (&fAsShotWhiteXY);
2214
2215 }
2216
2217 directory.Add (&fLinearResponseLimit);
2218
2219 }
2220
2221 }
2222
2223/******************************************************************************/
2224
2225class profile_tag_set
2226 {
2227
2228 private:
2229
2230 tag_uint16 fCalibrationIlluminant1;
2231 tag_uint16 fCalibrationIlluminant2;
2232
2233 tag_matrix fColorMatrix1;
2234 tag_matrix fColorMatrix2;
2235
2236 tag_matrix fForwardMatrix1;
2237 tag_matrix fForwardMatrix2;
2238
2239 tag_matrix fReductionMatrix1;
2240 tag_matrix fReductionMatrix2;
2241
2242 tag_string fProfileName;
2243
2244 tag_string fProfileCalibrationSignature;
2245
2246 tag_uint32 fEmbedPolicyTag;
2247
2248 tag_string fCopyrightTag;
2249
2250 uint32 fHueSatMapDimData [3];
2251
2252 tag_uint32_ptr fHueSatMapDims;
2253
2254 tag_data_ptr fHueSatData1;
2255 tag_data_ptr fHueSatData2;
2256
2257 tag_uint32 fHueSatMapEncodingTag;
2258
2259 uint32 fLookTableDimData [3];
2260
2261 tag_uint32_ptr fLookTableDims;
2262
2263 tag_data_ptr fLookTableData;
2264
2265 tag_uint32 fLookTableEncodingTag;
2266
2267 tag_srational fBaselineExposureOffsetTag;
2268
2269 tag_uint32 fDefaultBlackRenderTag;
2270
2271 dng_memory_data fToneCurveBuffer;
2272
2273 tag_data_ptr fToneCurveTag;
2274
2275 public:
2276
2277 profile_tag_set (dng_tiff_directory &directory,
2278 const dng_camera_profile &profile);
2279
2280 };
2281
2282/******************************************************************************/
2283
2284profile_tag_set::profile_tag_set (dng_tiff_directory &directory,
2285 const dng_camera_profile &profile)
2286
2287 : fCalibrationIlluminant1 (tcCalibrationIlluminant1,
2288 (uint16) profile.CalibrationIlluminant1 ())
2289
2290 , fCalibrationIlluminant2 (tcCalibrationIlluminant2,
2291 (uint16) profile.CalibrationIlluminant2 ())
2292
2293 , fColorMatrix1 (tcColorMatrix1,
2294 profile.ColorMatrix1 ())
2295
2296 , fColorMatrix2 (tcColorMatrix2,
2297 profile.ColorMatrix2 ())
2298
2299 , fForwardMatrix1 (tcForwardMatrix1,
2300 profile.ForwardMatrix1 ())
2301
2302 , fForwardMatrix2 (tcForwardMatrix2,
2303 profile.ForwardMatrix2 ())
2304
2305 , fReductionMatrix1 (tcReductionMatrix1,
2306 profile.ReductionMatrix1 ())
2307
2308 , fReductionMatrix2 (tcReductionMatrix2,
2309 profile.ReductionMatrix2 ())
2310
2311 , fProfileName (tcProfileName,
2312 profile.Name (),
2313 false)
2314
2315 , fProfileCalibrationSignature (tcProfileCalibrationSignature,
2316 profile.ProfileCalibrationSignature (),
2317 false)
2318
2319 , fEmbedPolicyTag (tcProfileEmbedPolicy,
2320 profile.EmbedPolicy ())
2321
2322 , fCopyrightTag (tcProfileCopyright,
2323 profile.Copyright (),
2324 false)
2325
2326 , fHueSatMapDims (tcProfileHueSatMapDims,
2327 fHueSatMapDimData,
2328 3)
2329
2330 , fHueSatData1 (tcProfileHueSatMapData1,
2331 ttFloat,
2332 profile.HueSatDeltas1 ().DeltasCount () * 3,
2333 profile.HueSatDeltas1 ().GetConstDeltas ())
2334
2335 , fHueSatData2 (tcProfileHueSatMapData2,
2336 ttFloat,
2337 profile.HueSatDeltas2 ().DeltasCount () * 3,
2338 profile.HueSatDeltas2 ().GetConstDeltas ())
2339
2340 , fHueSatMapEncodingTag (tcProfileHueSatMapEncoding,
2341 profile.HueSatMapEncoding ())
2342
2343 , fLookTableDims (tcProfileLookTableDims,
2344 fLookTableDimData,
2345 3)
2346
2347 , fLookTableData (tcProfileLookTableData,
2348 ttFloat,
2349 profile.LookTable ().DeltasCount () * 3,
2350 profile.LookTable ().GetConstDeltas ())
2351
2352 , fLookTableEncodingTag (tcProfileLookTableEncoding,
2353 profile.LookTableEncoding ())
2354
2355 , fBaselineExposureOffsetTag (tcBaselineExposureOffset,
2356 profile.BaselineExposureOffset ())
2357
2358 , fDefaultBlackRenderTag (tcDefaultBlackRender,
2359 profile.DefaultBlackRender ())
2360
2361 , fToneCurveBuffer ()
2362
2363 , fToneCurveTag (tcProfileToneCurve,
2364 ttFloat,
2365 0,
2366 NULL)
2367
2368 {
2369
2370 if (profile.HasColorMatrix1 ())
2371 {
2372
2373 uint32 colorChannels = profile.ColorMatrix1 ().Rows ();
2374
2375 directory.Add (&fCalibrationIlluminant1);
2376
2377 directory.Add (&fColorMatrix1);
2378
2379 if (fForwardMatrix1.Count () == colorChannels * 3)
2380 {
2381
2382 directory.Add (&fForwardMatrix1);
2383
2384 }
2385
2386 if (colorChannels > 3 && fReductionMatrix1.Count () == colorChannels * 3)
2387 {
2388
2389 directory.Add (&fReductionMatrix1);
2390
2391 }
2392
2393 if (profile.HasColorMatrix2 ())
2394 {
2395
2396 directory.Add (&fCalibrationIlluminant2);
2397
2398 directory.Add (&fColorMatrix2);
2399
2400 if (fForwardMatrix2.Count () == colorChannels * 3)
2401 {
2402
2403 directory.Add (&fForwardMatrix2);
2404
2405 }
2406
2407 if (colorChannels > 3 && fReductionMatrix2.Count () == colorChannels * 3)
2408 {
2409
2410 directory.Add (&fReductionMatrix2);
2411
2412 }
2413
2414 }
2415
2416 if (profile.Name ().NotEmpty ())
2417 {
2418
2419 directory.Add (&fProfileName);
2420
2421 }
2422
2423 if (profile.ProfileCalibrationSignature ().NotEmpty ())
2424 {
2425
2426 directory.Add (&fProfileCalibrationSignature);
2427
2428 }
2429
2430 directory.Add (&fEmbedPolicyTag);
2431
2432 if (profile.Copyright ().NotEmpty ())
2433 {
2434
2435 directory.Add (&fCopyrightTag);
2436
2437 }
2438
2439 bool haveHueSat1 = profile.HueSatDeltas1 ().IsValid ();
2440
2441 bool haveHueSat2 = profile.HueSatDeltas2 ().IsValid () &&
2442 profile.HasColorMatrix2 ();
2443
2444 if (haveHueSat1 || haveHueSat2)
2445 {
2446
2447 uint32 hueDivs = 0;
2448 uint32 satDivs = 0;
2449 uint32 valDivs = 0;
2450
2451 if (haveHueSat1)
2452 {
2453
2454 profile.HueSatDeltas1 ().GetDivisions (hueDivs,
2455 satDivs,
2456 valDivs);
2457
2458 }
2459
2460 else
2461 {
2462
2463 profile.HueSatDeltas2 ().GetDivisions (hueDivs,
2464 satDivs,
2465 valDivs);
2466
2467 }
2468
2469 fHueSatMapDimData [0] = hueDivs;
2470 fHueSatMapDimData [1] = satDivs;
2471 fHueSatMapDimData [2] = valDivs;
2472
2473 directory.Add (&fHueSatMapDims);
2474
2475 // Don't bother including the ProfileHueSatMapEncoding tag unless it's
2476 // non-linear.
2477
2478 if (profile.HueSatMapEncoding () != encoding_Linear)
2479 {
2480
2481 directory.Add (&fHueSatMapEncodingTag);
2482
2483 }
2484
2485 }
2486
2487 if (haveHueSat1)
2488 {
2489
2490 directory.Add (&fHueSatData1);
2491
2492 }
2493
2494 if (haveHueSat2)
2495 {
2496
2497 directory.Add (&fHueSatData2);
2498
2499 }
2500
2501 if (profile.HasLookTable ())
2502 {
2503
2504 uint32 hueDivs = 0;
2505 uint32 satDivs = 0;
2506 uint32 valDivs = 0;
2507
2508 profile.LookTable ().GetDivisions (hueDivs,
2509 satDivs,
2510 valDivs);
2511
2512 fLookTableDimData [0] = hueDivs;
2513 fLookTableDimData [1] = satDivs;
2514 fLookTableDimData [2] = valDivs;
2515
2516 directory.Add (&fLookTableDims);
2517
2518 directory.Add (&fLookTableData);
2519
2520 // Don't bother including the ProfileLookTableEncoding tag unless it's
2521 // non-linear.
2522
2523 if (profile.LookTableEncoding () != encoding_Linear)
2524 {
2525
2526 directory.Add (&fLookTableEncodingTag);
2527
2528 }
2529
2530 }
2531
2532 // Don't bother including the BaselineExposureOffset tag unless it's both
2533 // valid and non-zero.
2534
2535 if (profile.BaselineExposureOffset ().IsValid ())
2536 {
2537
2538 if (profile.BaselineExposureOffset ().As_real64 () != 0.0)
2539 {
2540
2541 directory.Add (&fBaselineExposureOffsetTag);
2542
2543 }
2544
2545 }
2546
2547 if (profile.DefaultBlackRender () != defaultBlackRender_Auto)
2548 {
2549
2550 directory.Add (&fDefaultBlackRenderTag);
2551
2552 }
2553
2554 if (profile.ToneCurve ().IsValid ())
2555 {
2556
2557 // Tone curve stored as pairs of 32-bit coordinates. Probably could do with
2558 // 16-bits here, but should be small number of points so...
2559
2560 uint32 toneCurvePoints = (uint32) (profile.ToneCurve ().fCoord.size ());
2561
2562 fToneCurveBuffer.Allocate (SafeUint32Mult(toneCurvePoints, 2),
2563 sizeof (real32));
2564
2565 real32 *points = fToneCurveBuffer.Buffer_real32 ();
2566
2567 fToneCurveTag.SetCount (toneCurvePoints * 2);
2568 fToneCurveTag.SetData (points);
2569
2570 for (uint32 i = 0; i < toneCurvePoints; i++)
2571 {
2572
2573 // Transpose coordinates so they are in a more expected
2574 // order (domain -> range).
2575
2576 points [i * 2 ] = (real32) profile.ToneCurve ().fCoord [i].h;
2577 points [i * 2 + 1] = (real32) profile.ToneCurve ().fCoord [i].v;
2578
2579 }
2580
2581 directory.Add (&fToneCurveTag);
2582
2583 }
2584
2585 }
2586
2587 }
2588
2589/******************************************************************************/
2590
2591tiff_dng_extended_color_profile::tiff_dng_extended_color_profile
2592 (const dng_camera_profile &profile)
2593
2594 : fProfile (profile)
2595
2596 {
2597
2598 }
2599
2600/******************************************************************************/
2601
2602void tiff_dng_extended_color_profile::Put (dng_stream &stream,
2603 bool includeModelRestriction)
2604 {
2605
2606 // Profile header.
2607
2608 stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII);
2609
2610 stream.Put_uint16 (magicExtendedProfile);
2611
2612 stream.Put_uint32 (8);
2613
2614 // Profile tags.
2615
2616 profile_tag_set tagSet (*this, fProfile);
2617
2618 // Camera this profile is for.
2619
2620 tag_string cameraModelTag (tcUniqueCameraModel,
2621 fProfile.UniqueCameraModelRestriction ());
2622
2623 if (includeModelRestriction)
2624 {
2625
2626 if (fProfile.UniqueCameraModelRestriction ().NotEmpty ())
2627 {
2628
2629 Add (&cameraModelTag);
2630
2631 }
2632
2633 }
2634
2635 // Write it all out.
2636
2637 dng_tiff_directory::Put (stream, offsetsRelativeToExplicitBase, 8);
2638
2639 }
2640
2641/*****************************************************************************/
2642
2643tag_dng_noise_profile::tag_dng_noise_profile (const dng_noise_profile &profile)
2644
2645 : tag_data_ptr (tcNoiseProfile,
2646 ttDouble,
2647 2 * profile.NumFunctions (),
2648 fValues)
2649
2650 {
2651
2652 DNG_REQUIRE (profile.NumFunctions () <= kMaxColorPlanes,
2653 "Too many noise functions in tag_dng_noise_profile.");
2654
2655 for (uint32 i = 0; i < profile.NumFunctions (); i++)
2656 {
2657
2658 fValues [(2 * i) ] = profile.NoiseFunction (i).Scale ();
2659 fValues [(2 * i) + 1] = profile.NoiseFunction (i).Offset ();
2660
2661 }
2662
2663 }
2664
2665/*****************************************************************************/
2666
2667dng_image_writer::dng_image_writer ()
2668 {
2669
2670 }
2671
2672/*****************************************************************************/
2673
2674dng_image_writer::~dng_image_writer ()
2675 {
2676
2677 }
2678
2679/*****************************************************************************/
2680
2681uint32 dng_image_writer::CompressedBufferSize (const dng_ifd &ifd,
2682 uint32 uncompressedSize)
2683 {
2684
2685 switch (ifd.fCompression)
2686 {
2687
2688 case ccLZW:
2689 {
2690
2691 // Add lots of slop for LZW to expand data.
2692
2693 return SafeUint32Add (SafeUint32Mult (uncompressedSize, 2), 1024);
2694
2695 }
2696
2697 case ccDeflate:
2698 {
2699
2700 // ZLib says maximum is source size + 0.1% + 12 bytes.
2701
2702 return SafeUint32Add (SafeUint32Add (uncompressedSize,
2703 uncompressedSize >> 8), 64);
2704
2705 }
2706
2707 case ccJPEG:
2708 {
2709
2710 // If we are saving lossless JPEG from an 8-bit image, reserve
2711 // space to pad the data out to 16-bits.
2712
2713 if (ifd.fBitsPerSample [0] <= 8)
2714 {
2715
2716 return SafeUint32Mult (uncompressedSize, 2);
2717
2718 }
2719
2720 break;
2721
2722 }
2723
2724 default:
2725 break;
2726
2727 }
2728
2729 return 0;
2730
2731 }
2732
2733/******************************************************************************/
2734
2735static void EncodeDelta8 (uint8 *dPtr,
2736 uint32 rows,
2737 uint32 cols,
2738 uint32 channels)
2739 {
2740
2741 const uint32 dRowStep = cols * channels;
2742
2743 for (uint32 row = 0; row < rows; row++)
2744 {
2745
2746 for (uint32 col = cols - 1; col > 0; col--)
2747 {
2748
2749 for (uint32 channel = 0; channel < channels; channel++)
2750 {
2751
2752 dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
2753
2754 }
2755
2756 }
2757
2758 dPtr += dRowStep;
2759
2760 }
2761
2762 }
2763
2764/******************************************************************************/
2765
2766static void EncodeDelta16 (uint16 *dPtr,
2767 uint32 rows,
2768 uint32 cols,
2769 uint32 channels)
2770 {
2771
2772 const uint32 dRowStep = cols * channels;
2773
2774 for (uint32 row = 0; row < rows; row++)
2775 {
2776
2777 for (uint32 col = cols - 1; col > 0; col--)
2778 {
2779
2780 for (uint32 channel = 0; channel < channels; channel++)
2781 {
2782
2783 dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
2784
2785 }
2786
2787 }
2788
2789 dPtr += dRowStep;
2790
2791 }
2792
2793 }
2794
2795/******************************************************************************/
2796
2797static void EncodeDelta32 (uint32 *dPtr,
2798 uint32 rows,
2799 uint32 cols,
2800 uint32 channels)
2801 {
2802
2803 const uint32 dRowStep = cols * channels;
2804
2805 for (uint32 row = 0; row < rows; row++)
2806 {
2807
2808 for (uint32 col = cols - 1; col > 0; col--)
2809 {
2810
2811 for (uint32 channel = 0; channel < channels; channel++)
2812 {
2813
2814 dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel];
2815
2816 }
2817
2818 }
2819
2820 dPtr += dRowStep;
2821
2822 }
2823
2824 }
2825
2826/*****************************************************************************/
2827
2828inline void EncodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels)
2829 {
2830
2831 if (channels == 1)
2832 {
2833
2834 bytePtr += (cols - 1);
2835
2836 uint8 this0 = bytePtr [0];
2837
2838 for (int32 col = 1; col < cols; col++)
2839 {
2840
2841 uint8 prev0 = bytePtr [-1];
2842
2843 this0 -= prev0;
2844
2845 bytePtr [0] = this0;
2846
2847 this0 = prev0;
2848
2849 bytePtr -= 1;
2850
2851 }
2852
2853 }
2854
2855 else if (channels == 3)
2856 {
2857
2858 bytePtr += (cols - 1) * 3;
2859
2860 uint8 this0 = bytePtr [0];
2861 uint8 this1 = bytePtr [1];
2862 uint8 this2 = bytePtr [2];
2863
2864 for (int32 col = 1; col < cols; col++)
2865 {
2866
2867 uint8 prev0 = bytePtr [-3];
2868 uint8 prev1 = bytePtr [-2];
2869 uint8 prev2 = bytePtr [-1];
2870
2871 this0 -= prev0;
2872 this1 -= prev1;
2873 this2 -= prev2;
2874
2875 bytePtr [0] = this0;
2876 bytePtr [1] = this1;
2877 bytePtr [2] = this2;
2878
2879 this0 = prev0;
2880 this1 = prev1;
2881 this2 = prev2;
2882
2883 bytePtr -= 3;
2884
2885 }
2886
2887 }
2888
2889 else
2890 {
2891
2892 uint32 rowBytes = cols * channels;
2893
2894 bytePtr += rowBytes - 1;
2895
2896 for (uint32 col = channels; col < rowBytes; col++)
2897 {
2898
2899 bytePtr [0] -= bytePtr [-channels];
2900
2901 bytePtr--;
2902
2903 }
2904
2905 }
2906
2907 }
2908
2909/*****************************************************************************/
2910
2911static void EncodeFPDelta (uint8 *buffer,
2912 uint8 *temp,
2913 int32 cols,
2914 int32 channels,
2915 int32 bytesPerSample)
2916 {
2917
2918 int32 rowIncrement = cols * channels;
2919
2920 if (bytesPerSample == 2)
2921 {
2922
2923 const uint8 *src = buffer;
2924
2925 #if qDNGBigEndian
2926 uint8 *dst0 = temp;
2927 uint8 *dst1 = temp + rowIncrement;
2928 #else
2929 uint8 *dst1 = temp;
2930 uint8 *dst0 = temp + rowIncrement;
2931 #endif
2932
2933 for (int32 col = 0; col < rowIncrement; ++col)
2934 {
2935
2936 dst0 [col] = src [0];
2937 dst1 [col] = src [1];
2938
2939 src += 2;
2940
2941 }
2942
2943 }
2944
2945 else if (bytesPerSample == 3)
2946 {
2947
2948 const uint8 *src = buffer;
2949
2950 uint8 *dst0 = temp;
2951 uint8 *dst1 = temp + rowIncrement;
2952 uint8 *dst2 = temp + rowIncrement * 2;
2953
2954 for (int32 col = 0; col < rowIncrement; ++col)
2955 {
2956
2957 dst0 [col] = src [0];
2958 dst1 [col] = src [1];
2959 dst2 [col] = src [2];
2960
2961 src += 3;
2962
2963 }
2964
2965 }
2966
2967 else
2968 {
2969
2970 const uint8 *src = buffer;
2971
2972 #if qDNGBigEndian
2973 uint8 *dst0 = temp;
2974 uint8 *dst1 = temp + rowIncrement;
2975 uint8 *dst2 = temp + rowIncrement * 2;
2976 uint8 *dst3 = temp + rowIncrement * 3;
2977 #else
2978 uint8 *dst3 = temp;
2979 uint8 *dst2 = temp + rowIncrement;
2980 uint8 *dst1 = temp + rowIncrement * 2;
2981 uint8 *dst0 = temp + rowIncrement * 3;
2982 #endif
2983
2984 for (int32 col = 0; col < rowIncrement; ++col)
2985 {
2986
2987 dst0 [col] = src [0];
2988 dst1 [col] = src [1];
2989 dst2 [col] = src [2];
2990 dst3 [col] = src [3];
2991
2992 src += 4;
2993
2994 }
2995
2996 }
2997
2998 EncodeDeltaBytes (temp, cols*bytesPerSample, channels);
2999
3000 memcpy (buffer, temp, cols*bytesPerSample*channels);
3001
3002 }
3003
3004/*****************************************************************************/
3005
3006void dng_image_writer::EncodePredictor (dng_host &host,
3007 const dng_ifd &ifd,
3008 dng_pixel_buffer &buffer,
3009 AutoPtr<dng_memory_block> &tempBuffer)
3010 {
3011
3012 switch (ifd.fPredictor)
3013 {
3014
3015 case cpHorizontalDifference:
3016 case cpHorizontalDifferenceX2:
3017 case cpHorizontalDifferenceX4:
3018 {
3019
3020 int32 xFactor = 1;
3021
3022 if (ifd.fPredictor == cpHorizontalDifferenceX2)
3023 {
3024 xFactor = 2;
3025 }
3026
3027 else if (ifd.fPredictor == cpHorizontalDifferenceX4)
3028 {
3029 xFactor = 4;
3030 }
3031
3032 switch (buffer.fPixelType)
3033 {
3034
3035 case ttByte:
3036 {
3037
3038 EncodeDelta8 ((uint8 *) buffer.fData,
3039 buffer.fArea.H (),
3040 buffer.fArea.W () / xFactor,
3041 buffer.fPlanes * xFactor);
3042
3043 return;
3044
3045 }
3046
3047 case ttShort:
3048 {
3049
3050 EncodeDelta16 ((uint16 *) buffer.fData,
3051 buffer.fArea.H (),
3052 buffer.fArea.W () / xFactor,
3053 buffer.fPlanes * xFactor);
3054
3055 return;
3056
3057 }
3058
3059 case ttLong:
3060 {
3061
3062 EncodeDelta32 ((uint32 *) buffer.fData,
3063 buffer.fArea.H (),
3064 buffer.fArea.W () / xFactor,
3065 buffer.fPlanes * xFactor);
3066
3067 return;
3068
3069 }
3070
3071 default:
3072 break;
3073
3074 }
3075
3076 break;
3077
3078 }
3079
3080 case cpFloatingPoint:
3081 case cpFloatingPointX2:
3082 case cpFloatingPointX4:
3083 {
3084
3085 int32 xFactor = 1;
3086
3087 if (ifd.fPredictor == cpFloatingPointX2)
3088 {
3089 xFactor = 2;
3090 }
3091
3092 else if (ifd.fPredictor == cpFloatingPointX4)
3093 {
3094 xFactor = 4;
3095 }
3096
3097 if (buffer.fRowStep < 0)
3098 {
3099 ThrowProgramError ("Row step may not be negative");
3100 }
3101 uint32 tempBufferSize = SafeUint32Mult (
3102 static_cast<uint32>(buffer.fRowStep),
3103 buffer.fPixelSize);
3104
3105 if (!tempBuffer.Get () || tempBuffer->LogicalSize () < tempBufferSize)
3106 {
3107
3108 tempBuffer.Reset (host.Allocate (tempBufferSize));
3109
3110 }
3111
3112 for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++)
3113 {
3114
3115 EncodeFPDelta ((uint8 *) buffer.DirtyPixel (row, buffer.fArea.l, buffer.fPlane),
3116 tempBuffer->Buffer_uint8 (),
3117 buffer.fArea.W () / xFactor,
3118 buffer.fPlanes * xFactor,
3119 buffer.fPixelSize);
3120
3121 }
3122
3123 return;
3124
3125 }
3126
3127 default:
3128 break;
3129
3130 }
3131
3132 if (ifd.fPredictor != cpNullPredictor)
3133 {
3134
3135 ThrowProgramError ();
3136
3137 }
3138
3139 }
3140
3141/*****************************************************************************/
3142
3143void dng_image_writer::ByteSwapBuffer (dng_host & /* host */,
3144 dng_pixel_buffer &buffer)
3145 {
3146
3147 uint32 pixels = buffer.fRowStep * buffer.fArea.H ();
3148
3149 switch (buffer.fPixelSize)
3150 {
3151
3152 case 2:
3153 {
3154
3155 DoSwapBytes16 ((uint16 *) buffer.fData,
3156 pixels);
3157
3158 break;
3159
3160 }
3161
3162 case 4:
3163 {
3164
3165 DoSwapBytes32 ((uint32 *) buffer.fData,
3166 pixels);
3167
3168 break;
3169
3170 }
3171
3172 default:
3173 break;
3174
3175 }
3176
3177 }
3178
3179/*****************************************************************************/
3180
3181void dng_image_writer::ReorderSubTileBlocks (const dng_ifd &ifd,
3182 dng_pixel_buffer &buffer,
3183 AutoPtr<dng_memory_block> &uncompressedBuffer,
3184 AutoPtr<dng_memory_block> &subTileBlockBuffer)
3185 {
3186
3187 uint32 blockRows = ifd.fSubTileBlockRows;
3188 uint32 blockCols = ifd.fSubTileBlockCols;
3189
3190 uint32 rowBlocks = buffer.fArea.H () / blockRows;
3191 uint32 colBlocks = buffer.fArea.W () / blockCols;
3192
3193 int32 rowStep = buffer.fRowStep * buffer.fPixelSize;
3194 int32 colStep = buffer.fColStep * buffer.fPixelSize;
3195
3196 int32 rowBlockStep = rowStep * blockRows;
3197 int32 colBlockStep = colStep * blockCols;
3198
3199 uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize;
3200
3201 const uint8 *s0 = uncompressedBuffer->Buffer_uint8 ();
3202 uint8 *d0 = subTileBlockBuffer->Buffer_uint8 ();
3203
3204 for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++)
3205 {
3206
3207 const uint8 *s1 = s0;
3208
3209 for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++)
3210 {
3211
3212 const uint8 *s2 = s1;
3213
3214 for (uint32 blockRow = 0; blockRow < blockRows; blockRow++)
3215 {
3216
3217 for (uint32 j = 0; j < blockColBytes; j++)
3218 {
3219
3220 d0 [j] = s2 [j];
3221
3222 }
3223
3224 d0 += blockColBytes;
3225
3226 s2 += rowStep;
3227
3228 }
3229
3230 s1 += colBlockStep;
3231
3232 }
3233
3234 s0 += rowBlockStep;
3235
3236 }
3237
3238 // Copy back reordered pixels.
3239
3240 DoCopyBytes (subTileBlockBuffer->Buffer (),
3241 uncompressedBuffer->Buffer (),
3242 uncompressedBuffer->LogicalSize ());
3243
3244 }
3245
3246/******************************************************************************/
3247
3248class dng_lzw_compressor
3249 {
3250
3251 private:
3252
3253 enum
3254 {
3255 kResetCode = 256,
3256 kEndCode = 257,
3257 kTableSize = 4096
3258 };
3259
3260 // Compressor nodes have two son pointers. The low order bit of
3261 // the next code determines which pointer is used. This cuts the
3262 // number of nodes searched for the next code by two on average.
3263
3264 struct LZWCompressorNode
3265 {
3266 int16 final;
3267 int16 son0;
3268 int16 son1;
3269 int16 brother;
3270 };
3271
3272 dng_memory_data fBuffer;
3273
3274 LZWCompressorNode *fTable;
3275
3276 uint8 *fDstPtr;
3277
3278 int32 fDstCount;
3279
3280 int32 fBitOffset;
3281
3282 int32 fNextCode;
3283
3284 int32 fCodeSize;
3285
3286 public:
3287
3288 dng_lzw_compressor ();
3289
3290 void Compress (const uint8 *sPtr,
3291 uint8 *dPtr,
3292 uint32 sCount,
3293 uint32 &dCount);
3294
3295 private:
3296
3297 void InitTable ();
3298
3299 int32 SearchTable (int32 w, int32 k) const
3300 {
3301
3302 DNG_ASSERT ((w >= 0) && (w <= kTableSize),
3303 "Bad w value in dng_lzw_compressor::SearchTable");
3304
3305 int32 son0 = fTable [w] . son0;
3306 int32 son1 = fTable [w] . son1;
3307
3308 // Branchless version of:
3309 // int32 code = (k & 1) ? son1 : son0;
3310
3311 int32 code = son0 + ((-((int32) (k & 1))) & (son1 - son0));
3312
3313 while (code > 0 && fTable [code].final != k)
3314 {
3315 code = fTable [code].brother;
3316 }
3317
3318 return code;
3319
3320 }
3321
3322 void AddTable (int32 w, int32 k);
3323
3324 void PutCodeWord (int32 code);
3325
3326 // Hidden copy constructor and assignment operator.
3327
3328 dng_lzw_compressor (const dng_lzw_compressor &compressor);
3329
3330 dng_lzw_compressor & operator= (const dng_lzw_compressor &compressor);
3331
3332 };
3333
3334/******************************************************************************/
3335
3336dng_lzw_compressor::dng_lzw_compressor ()
3337
3338 : fBuffer ()
3339 , fTable (NULL)
3340 , fDstPtr (NULL)
3341 , fDstCount (0)
3342 , fBitOffset (0)
3343 , fNextCode (0)
3344 , fCodeSize (0)
3345
3346 {
3347
3348 fBuffer.Allocate (kTableSize, sizeof (LZWCompressorNode));
3349
3350 fTable = (LZWCompressorNode *) fBuffer.Buffer ();
3351
3352 }
3353
3354/******************************************************************************/
3355
3356void dng_lzw_compressor::InitTable ()
3357 {
3358
3359 fCodeSize = 9;
3360
3361 fNextCode = 258;
3362
3363 LZWCompressorNode *node = &fTable [0];
3364
3365 for (int32 code = 0; code < 256; ++code)
3366 {
3367
3368 node->final = (int16) code;
3369 node->son0 = -1;
3370 node->son1 = -1;
3371 node->brother = -1;
3372
3373 node++;
3374
3375 }
3376
3377 }
3378
3379/******************************************************************************/
3380
3381void dng_lzw_compressor::AddTable (int32 w, int32 k)
3382 {
3383
3384 DNG_ASSERT ((w >= 0) && (w <= kTableSize),
3385 "Bad w value in dng_lzw_compressor::AddTable");
3386
3387 LZWCompressorNode *node = &fTable [w];
3388
3389 int32 nextCode = fNextCode;
3390
3391 DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize),
3392 "Bad fNextCode value in dng_lzw_compressor::AddTable");
3393
3394 LZWCompressorNode *node2 = &fTable [nextCode];
3395
3396 fNextCode++;
3397
3398 int32 oldSon;
3399
3400 if( k&1 )
3401 {
3402 oldSon = node->son1;
3403 node->son1 = (int16) nextCode;
3404 }
3405 else
3406 {
3407 oldSon = node->son0;
3408 node->son0 = (int16) nextCode;
3409 }
3410
3411 node2->final = (int16) k;
3412 node2->son0 = -1;
3413 node2->son1 = -1;
3414 node2->brother = (int16) oldSon;
3415
3416 if (nextCode == (1 << fCodeSize) - 1)
3417 {
3418 if (fCodeSize != 12)
3419 fCodeSize++;
3420 }
3421
3422 }
3423
3424/******************************************************************************/
3425
3426void dng_lzw_compressor::PutCodeWord (int32 code)
3427 {
3428
3429 int32 bit = (int32) (fBitOffset & 7);
3430
3431 int32 offset1 = fBitOffset >> 3;
3432 int32 offset2 = (fBitOffset + fCodeSize - 1) >> 3;
3433
3434 int32 shift1 = (fCodeSize + bit) - 8;
3435 int32 shift2 = (fCodeSize + bit) - 16;
3436
3437 uint8 byte1 = (uint8) (code >> shift1);
3438
3439 uint8 *dstPtr1 = fDstPtr + offset1;
3440 uint8 *dstPtr3 = fDstPtr + offset2;
3441
3442 if (offset1 + 1 == offset2)
3443 {
3444
3445 uint8 byte2 = (uint8) (code << (-shift2));
3446
3447 if (bit)
3448 *dstPtr1 |= byte1;
3449 else
3450 *dstPtr1 = byte1;
3451
3452 *dstPtr3 = byte2;
3453
3454 }
3455
3456 else
3457 {
3458
3459 int32 shift3 = (fCodeSize + bit) - 24;
3460
3461 uint8 byte2 = (uint8) (code >> shift2);
3462 uint8 byte3 = (uint8) (code << (-shift3));
3463
3464 uint8 *dstPtr2 = fDstPtr + (offset1 + 1);
3465
3466 if (bit)
3467 *dstPtr1 |= byte1;
3468 else
3469 *dstPtr1 = byte1;
3470
3471 *dstPtr2 = byte2;
3472
3473 *dstPtr3 = byte3;
3474
3475 }
3476
3477 fBitOffset += fCodeSize;
3478
3479 }
3480
3481/******************************************************************************/
3482
3483void dng_lzw_compressor::Compress (const uint8 *sPtr,
3484 uint8 *dPtr,
3485 uint32 sCount,
3486 uint32 &dCount)
3487 {
3488
3489 fDstPtr = dPtr;
3490
3491 fBitOffset = 0;
3492
3493 InitTable ();
3494
3495 PutCodeWord (kResetCode);
3496
3497 int32 code = -1;
3498
3499 int32 pixel;
3500
3501 if (sCount > 0)
3502 {
3503
3504 pixel = *sPtr;
3505 sPtr = sPtr + 1;
3506 code = pixel;
3507
3508 sCount--;
3509
3510 while (sCount--)
3511 {
3512
3513 pixel = *sPtr;
3514 sPtr = sPtr + 1;
3515
3516 int32 newCode = SearchTable (code, pixel);
3517
3518 if (newCode == -1)
3519 {
3520
3521 PutCodeWord (code);
3522
3523 if (fNextCode < 4093)
3524 {
3525 AddTable (code, pixel);
3526 }
3527 else
3528 {
3529 PutCodeWord (kResetCode);
3530 InitTable ();
3531 }
3532
3533 code = pixel;
3534
3535 }
3536
3537 else
3538 code = newCode;
3539
3540 }
3541
3542 }
3543
3544 if (code != -1)
3545 {
3546 PutCodeWord (code);
3547 AddTable (code, 0);
3548 }
3549
3550 PutCodeWord (kEndCode);
3551
3552 dCount = (fBitOffset + 7) >> 3;
3553
3554 }
3555
3556/*****************************************************************************/
3557
3558#if qDNGUseLibJPEG
3559
3560/*****************************************************************************/
3561
3562static void dng_error_exit (j_common_ptr cinfo)
3563 {
3564
3565 // Output message.
3566
3567 (*cinfo->err->output_message) (cinfo);
3568
3569 // Convert to a dng_exception.
3570
3571 switch (cinfo->err->msg_code)
3572 {
3573
3574 case JERR_OUT_OF_MEMORY:
3575 {
3576 ThrowMemoryFull ();
3577 break;
3578 }
3579
3580 default:
3581 {
3582 ThrowBadFormat ();
3583 }
3584
3585 }
3586
3587 }
3588
3589/*****************************************************************************/
3590
3591static void dng_output_message (j_common_ptr cinfo)
3592 {
3593
3594 // Format message to string.
3595
3596 char buffer [JMSG_LENGTH_MAX];
3597
3598 (*cinfo->err->format_message) (cinfo, buffer);
3599
3600 // Report the libjpeg message as a warning.
3601
3602 ReportWarning ("libjpeg", buffer);
3603
3604 }
3605
3606/*****************************************************************************/
3607
3608struct dng_jpeg_stream_dest
3609 {
3610
3611 struct jpeg_destination_mgr pub;
3612
3613 dng_stream *fStream;
3614
3615 uint8 fBuffer [4096];
3616
3617 };
3618
3619/*****************************************************************************/
3620
3621static void dng_init_destination (j_compress_ptr cinfo)
3622 {
3623
3624 dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest;
3625
3626 dest->pub.next_output_byte = dest->fBuffer;
3627 dest->pub.free_in_buffer = sizeof (dest->fBuffer);
3628
3629 }
3630
3631/*****************************************************************************/
3632
3633static boolean dng_empty_output_buffer (j_compress_ptr cinfo)
3634 {
3635
3636 dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest;
3637
3638 dest->fStream->Put (dest->fBuffer, sizeof (dest->fBuffer));
3639
3640 dest->pub.next_output_byte = dest->fBuffer;
3641 dest->pub.free_in_buffer = sizeof (dest->fBuffer);
3642
3643 return TRUE;
3644
3645 }
3646
3647/*****************************************************************************/
3648
3649static void dng_term_destination (j_compress_ptr cinfo)
3650 {
3651
3652 dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest;
3653
3654 uint32 datacount = sizeof (dest->fBuffer) -
3655 (uint32) dest->pub.free_in_buffer;
3656
3657 if (datacount)
3658 {
3659 dest->fStream->Put (dest->fBuffer, datacount);
3660 }
3661
3662 }
3663
3664/*****************************************************************************/
3665
3666static void jpeg_set_adobe_quality (struct jpeg_compress_struct *cinfo,
3667 int32 quality)
3668 {
3669
3670 // If out of range, map to default.
3671
3672 if (quality < 0 || quality > 12)
3673 {
3674 quality = 10;
3675 }
3676
3677 // Adobe turns off chroma downsampling at high quality levels.
3678
3679 bool useChromaDownsampling = (quality <= 6);
3680
3681 // Approximate mapping from Adobe quality levels to LibJPEG levels.
3682
3683 const int kLibJPEGQuality [13] =
3684 {
3685 5, 11, 23, 34, 46, 63, 76, 77, 86, 90, 94, 97, 99
3686 };
3687
3688 quality = kLibJPEGQuality [quality];
3689
3690 jpeg_set_quality (cinfo, quality, TRUE);
3691
3692 // LibJPEG defaults to always using chroma downsampling. Turn if off
3693 // if we need it off to match Adobe.
3694
3695 if (!useChromaDownsampling)
3696 {
3697
3698 cinfo->comp_info [0].h_samp_factor = 1;
3699 cinfo->comp_info [0].h_samp_factor = 1;
3700
3701 }
3702
3703 }
3704
3705/*****************************************************************************/
3706
3707#endif
3708
3709/*****************************************************************************/
3710
3711void dng_image_writer::WriteData (dng_host &host,
3712 const dng_ifd &ifd,
3713 dng_stream &stream,
3714 dng_pixel_buffer &buffer,
3715 AutoPtr<dng_memory_block> &compressedBuffer)
3716 {
3717
3718 switch (ifd.fCompression)
3719 {
3720
3721 case ccUncompressed:
3722 {
3723
3724 // Special case support for when we save to 8-bits from
3725 // 16-bit data.
3726
3727 if (ifd.fBitsPerSample [0] == 8 && buffer.fPixelType == ttShort)
3728 {
3729
3730 uint32 count = buffer.fRowStep *
3731 buffer.fArea.H ();
3732
3733 const uint16 *sPtr = (const uint16 *) buffer.fData;
3734
3735 for (uint32 j = 0; j < count; j++)
3736 {
3737
3738 stream.Put_uint8 ((uint8) sPtr [j]);
3739
3740 }
3741
3742 }
3743
3744 else
3745 {
3746
3747 // Swap bytes if required.
3748
3749 if (stream.SwapBytes ())
3750 {
3751
3752 ByteSwapBuffer (host, buffer);
3753
3754 }
3755
3756 // Write the bytes.
3757
3758 stream.Put (buffer.fData, buffer.fRowStep *
3759 buffer.fArea.H () *
3760 buffer.fPixelSize);
3761
3762 }
3763
3764 break;
3765
3766 }
3767
3768 case ccLZW:
3769 case ccDeflate:
3770 {
3771
3772 // Both these compression algorithms are byte based. The floating
3773 // point predictor already does byte ordering, so don't ever swap
3774 // when using it.
3775
3776 if (stream.SwapBytes () && ifd.fPredictor != cpFloatingPoint)
3777 {
3778
3779 ByteSwapBuffer (host,
3780 buffer);
3781
3782 }
3783
3784 // Run the compression algorithm.
3785
3786 uint32 sBytes = buffer.fRowStep *
3787 buffer.fArea.H () *
3788 buffer.fPixelSize;
3789
3790 uint8 *sBuffer = (uint8 *) buffer.fData;
3791
3792 uint32 dBytes = 0;
3793
3794 uint8 *dBuffer = compressedBuffer->Buffer_uint8 ();
3795
3796 if (ifd.fCompression == ccLZW)
3797 {
3798
3799 dng_lzw_compressor lzwCompressor;
3800
3801 lzwCompressor.Compress (sBuffer,
3802 dBuffer,
3803 sBytes,
3804 dBytes);
3805
3806 }
3807
3808 else
3809 {
3810
3811 uLongf dCount = compressedBuffer->LogicalSize ();
3812
3813 int32 level = Z_DEFAULT_COMPRESSION;
3814
3815 if (ifd.fCompressionQuality >= Z_BEST_SPEED &&
3816 ifd.fCompressionQuality <= Z_BEST_COMPRESSION)
3817 {
3818
3819 level = ifd.fCompressionQuality;
3820
3821 }
3822
3823 int zResult = ::compress2 (dBuffer,
3824 &dCount,
3825 sBuffer,
3826 sBytes,
3827 level);
3828
3829 if (zResult != Z_OK)
3830 {
3831
3832 ThrowMemoryFull ();
3833
3834 }
3835
3836 dBytes = (uint32) dCount;
3837
3838 }
3839
3840 if (dBytes > compressedBuffer->LogicalSize ())
3841 {
3842
3843 DNG_REPORT ("Compression output buffer overflow");
3844
3845 ThrowProgramError ();
3846
3847 }
3848
3849 stream.Put (dBuffer, dBytes);
3850
3851 return;
3852
3853 }
3854
3855 case ccJPEG:
3856 {
3857
3858 dng_pixel_buffer temp (buffer);
3859
3860 if (buffer.fPixelType == ttByte)
3861 {
3862
3863 // The lossless JPEG encoder needs 16-bit data, so if we are
3864 // are saving 8 bit data, we need to pad it out to 16-bits.
3865
3866 temp.fData = compressedBuffer->Buffer ();
3867
3868 temp.fPixelType = ttShort;
3869 temp.fPixelSize = 2;
3870
3871 temp.CopyArea (buffer,
3872 buffer.fArea,
3873 buffer.fPlane,
3874 buffer.fPlanes);
3875
3876 }
3877
3878 EncodeLosslessJPEG ((const uint16 *) temp.fData,
3879 temp.fArea.H (),
3880 temp.fArea.W (),
3881 temp.fPlanes,
3882 ifd.fBitsPerSample [0],
3883 temp.fRowStep,
3884 temp.fColStep,
3885 stream);
3886
3887 break;
3888
3889 }
3890
3891 #if qDNGUseLibJPEG
3892
3893 case ccLossyJPEG:
3894 {
3895
3896 struct jpeg_compress_struct cinfo;
3897
3898 // Setup the error manager.
3899
3900 struct jpeg_error_mgr jerr;
3901
3902 cinfo.err = jpeg_std_error (&jerr);
3903
3904 jerr.error_exit = dng_error_exit;
3905 jerr.output_message = dng_output_message;
3906
3907 try
3908 {
3909
3910 // Create the compression context.
3911
3912 jpeg_create_compress (&cinfo);
3913
3914 // Setup the destination manager to write to stream.
3915
3916 dng_jpeg_stream_dest dest;
3917
3918 dest.fStream = &stream;
3919
3920 dest.pub.init_destination = dng_init_destination;
3921 dest.pub.empty_output_buffer = dng_empty_output_buffer;
3922 dest.pub.term_destination = dng_term_destination;
3923
3924 cinfo.dest = &dest.pub;
3925
3926 // Setup basic image info.
3927
3928 cinfo.image_width = buffer.fArea.W ();
3929 cinfo.image_height = buffer.fArea.H ();
3930 cinfo.input_components = buffer.fPlanes;
3931
3932 switch (buffer.fPlanes)
3933 {
3934
3935 case 1:
3936 cinfo.in_color_space = JCS_GRAYSCALE;
3937 break;
3938
3939 case 3:
3940 cinfo.in_color_space = JCS_RGB;
3941 break;
3942
3943 case 4:
3944 cinfo.in_color_space = JCS_CMYK;
3945 break;
3946
3947 default:
3948 ThrowProgramError ();
3949
3950 }
3951
3952 // Setup the compression parameters.
3953
3954 jpeg_set_defaults (&cinfo);
3955
3956 jpeg_set_adobe_quality (&cinfo, ifd.fCompressionQuality);
3957
3958 // Write the JPEG header.
3959
3960 jpeg_start_compress (&cinfo, TRUE);
3961
3962 // Write the scanlines.
3963
3964 for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++)
3965 {
3966
3967 uint8 *sampArray [1];
3968
3969 sampArray [0] = buffer.DirtyPixel_uint8 (row,
3970 buffer.fArea.l,
3971 0);
3972
3973 jpeg_write_scanlines (&cinfo, sampArray, 1);
3974
3975 }
3976
3977 // Cleanup.
3978
3979 jpeg_finish_compress (&cinfo);
3980
3981 jpeg_destroy_compress (&cinfo);
3982
3983 }
3984
3985 catch (...)
3986 {
3987
3988 jpeg_destroy_compress (&cinfo);
3989
3990 throw;
3991
3992 }
3993
3994 return;
3995
3996 }
3997
3998 #endif
3999
4000 default:
4001 {
4002
4003 ThrowProgramError ();
4004
4005 }
4006
4007 }
4008
4009 }
4010
4011/******************************************************************************/
4012
4013void dng_image_writer::EncodeJPEGPreview (dng_host &host,
4014 const dng_image &image,
4015 dng_jpeg_preview &preview,
4016 int32 quality)
4017 {
4018
4019 #if qDNGUseLibJPEG
4020
4021 dng_memory_stream stream (host.Allocator ());
4022
4023 struct jpeg_compress_struct cinfo;
4024
4025 // Setup the error manager.
4026
4027 struct jpeg_error_mgr jerr;
4028
4029 cinfo.err = jpeg_std_error (&jerr);
4030
4031 jerr.error_exit = dng_error_exit;
4032 jerr.output_message = dng_output_message;
4033
4034 try
4035 {
4036
4037 // Create the compression context.
4038
4039 jpeg_create_compress (&cinfo);
4040
4041 // Setup the destination manager to write to stream.
4042
4043 dng_jpeg_stream_dest dest;
4044
4045 dest.fStream = &stream;
4046
4047 dest.pub.init_destination = dng_init_destination;
4048 dest.pub.empty_output_buffer = dng_empty_output_buffer;
4049 dest.pub.term_destination = dng_term_destination;
4050
4051 cinfo.dest = &dest.pub;
4052
4053 // Setup basic image info.
4054
4055 cinfo.image_width = image.Bounds ().W ();
4056 cinfo.image_height = image.Bounds ().H ();
4057 cinfo.input_components = image.Planes ();
4058
4059 switch (image.Planes ())
4060 {
4061
4062 case 1:
4063 cinfo.in_color_space = JCS_GRAYSCALE;
4064 break;
4065
4066 case 3:
4067 cinfo.in_color_space = JCS_RGB;
4068 break;
4069
4070 default:
4071 ThrowProgramError ();
4072
4073 }
4074
4075 // Setup the compression parameters.
4076
4077 jpeg_set_defaults (&cinfo);
4078
4079 jpeg_set_adobe_quality (&cinfo, quality);
4080
4081 // Find some preview information based on the compression settings.
4082
4083 preview.fPreviewSize = image.Size ();
4084
4085 if (image.Planes () == 1)
4086 {
4087
4088 preview.fPhotometricInterpretation = piBlackIsZero;
4089
4090 }
4091
4092 else
4093 {
4094
4095 preview.fPhotometricInterpretation = piYCbCr;
4096
4097 preview.fYCbCrSubSampling.h = cinfo.comp_info [0].h_samp_factor;
4098 preview.fYCbCrSubSampling.v = cinfo.comp_info [0].v_samp_factor;
4099
4100 }
4101
4102 // Write the JPEG header.
4103
4104 jpeg_start_compress (&cinfo, TRUE);
4105
4106 // Write the scanlines.
4107
4108 dng_pixel_buffer buffer (image.Bounds (), 0, image.Planes (), ttByte,
4109 pcInterleaved, NULL);
4110
4111 AutoPtr<dng_memory_block> bufferData (host.Allocate (buffer.fRowStep));
4112
4113 buffer.fData = bufferData->Buffer ();
4114
4115 for (uint32 row = 0; row < cinfo.image_height; row++)
4116 {
4117
4118 buffer.fArea.t = row;
4119 buffer.fArea.b = row + 1;
4120
4121 image.Get (buffer);
4122
4123 uint8 *sampArray [1];
4124
4125 sampArray [0] = buffer.DirtyPixel_uint8 (row,
4126 buffer.fArea.l,
4127 0);
4128
4129 jpeg_write_scanlines (&cinfo, sampArray, 1);
4130
4131 }
4132
4133 // Cleanup.
4134
4135 jpeg_finish_compress (&cinfo);
4136
4137 jpeg_destroy_compress (&cinfo);
4138
4139 }
4140
4141 catch (...)
4142 {
4143
4144 jpeg_destroy_compress (&cinfo);
4145
4146 throw;
4147
4148 }
4149
4150 preview.fCompressedData.Reset (stream.AsMemoryBlock (host.Allocator ()));
4151
4152 #else
4153
4154 (void) host;
4155 (void) image;
4156 (void) preview;
4157 (void) quality;
4158
4159 ThrowProgramError ("No JPEG encoder");
4160
4161 #endif
4162
4163 }
4164
4165/*****************************************************************************/
4166
4167void dng_image_writer::WriteTile (dng_host &host,
4168 const dng_ifd &ifd,
4169 dng_stream &stream,
4170 const dng_image &image,
4171 const dng_rect &tileArea,
4172 uint32 fakeChannels,
4173 AutoPtr<dng_memory_block> &compressedBuffer,
4174 AutoPtr<dng_memory_block> &uncompressedBuffer,
4175 AutoPtr<dng_memory_block> &subTileBlockBuffer,
4176 AutoPtr<dng_memory_block> &tempBuffer)
4177 {
4178
4179 // Create pixel buffer to hold uncompressed tile.
4180
4181 dng_pixel_buffer buffer (tileArea, 0, ifd.fSamplesPerPixel,
4182 image.PixelType(), pcInterleaved, uncompressedBuffer->Buffer());
4183
4184 // Get the uncompressed data.
4185
4186 image.Get (buffer, dng_image::edge_zero);
4187
4188 // Deal with sub-tile blocks.
4189
4190 if (ifd.fSubTileBlockRows > 1)
4191 {
4192
4193 ReorderSubTileBlocks (ifd,
4194 buffer,
4195 uncompressedBuffer,
4196 subTileBlockBuffer);
4197
4198 }
4199
4200 // Floating point depth conversion.
4201
4202 if (ifd.fSampleFormat [0] == sfFloatingPoint)
4203 {
4204
4205 if (ifd.fBitsPerSample [0] == 16)
4206 {
4207
4208 uint32 *srcPtr = (uint32 *) buffer.fData;
4209 uint16 *dstPtr = (uint16 *) buffer.fData;
4210
4211 uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes;
4212
4213 for (uint32 j = 0; j < pixels; j++)
4214 {
4215
4216 dstPtr [j] = DNG_FloatToHalf (srcPtr [j]);
4217
4218 }
4219
4220 buffer.fPixelSize = 2;
4221
4222 }
4223
4224 if (ifd.fBitsPerSample [0] == 24)
4225 {
4226
4227 uint32 *srcPtr = (uint32 *) buffer.fData;
4228 uint8 *dstPtr = (uint8 *) buffer.fData;
4229
4230 uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes;
4231
4232 if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint ||
4233 ifd.fPredictor == cpFloatingPointX2 ||
4234 ifd.fPredictor == cpFloatingPointX4)
4235 {
4236
4237 for (uint32 j = 0; j < pixels; j++)
4238 {
4239
4240 DNG_FloatToFP24 (srcPtr [j], dstPtr);
4241
4242 dstPtr += 3;
4243
4244 }
4245
4246 }
4247
4248 else
4249 {
4250
4251 for (uint32 j = 0; j < pixels; j++)
4252 {
4253
4254 uint8 output [3];
4255
4256 DNG_FloatToFP24 (srcPtr [j], output);
4257
4258 dstPtr [0] = output [2];
4259 dstPtr [1] = output [1];
4260 dstPtr [2] = output [0];
4261
4262 dstPtr += 3;
4263
4264 }
4265
4266 }
4267
4268 buffer.fPixelSize = 3;
4269
4270 }
4271
4272 }
4273
4274 // Run predictor.
4275
4276 EncodePredictor (host,
4277 ifd,
4278 buffer,
4279 tempBuffer);
4280
4281 // Adjust pixel buffer for fake channels.
4282
4283 if (fakeChannels > 1)
4284 {
4285
4286 buffer.fPlanes *= fakeChannels;
4287 buffer.fColStep *= fakeChannels;
4288
4289 buffer.fArea.r = buffer.fArea.l + (buffer.fArea.W () / fakeChannels);
4290
4291 }
4292
4293 // Compress (if required) and write out the data.
4294
4295 WriteData (host,
4296 ifd,
4297 stream,
4298 buffer,
4299 compressedBuffer);
4300
4301 }
4302
4303/*****************************************************************************/
4304
4305class dng_write_tiles_task : public dng_area_task
4306 {
4307
4308 private:
4309
4310 dng_image_writer &fImageWriter;
4311
4312 dng_host &fHost;
4313
4314 const dng_ifd &fIFD;
4315
4316 dng_basic_tag_set &fBasic;
4317
4318 dng_stream &fStream;
4319
4320 const dng_image &fImage;
4321
4322 uint32 fFakeChannels;
4323
4324 uint32 fTilesDown;
4325
4326 uint32 fTilesAcross;
4327
4328 uint32 fCompressedSize;
4329
4330 uint32 fUncompressedSize;
4331
4332 dng_mutex fMutex1;
4333
4334 uint32 fNextTileIndex;
4335
4336 dng_mutex fMutex2;
4337
4338 dng_condition fCondition;
4339
4340 bool fTaskFailed;
4341
4342 uint32 fWriteTileIndex;
4343
4344 public:
4345
4346 dng_write_tiles_task (dng_image_writer &imageWriter,
4347 dng_host &host,
4348 const dng_ifd &ifd,
4349 dng_basic_tag_set &basic,
4350 dng_stream &stream,
4351 const dng_image &image,
4352 uint32 fakeChannels,
4353 uint32 tilesDown,
4354 uint32 tilesAcross,
4355 uint32 compressedSize,
4356 uint32 uncompressedSize)
4357
4358 : fImageWriter (imageWriter)
4359 , fHost (host)
4360 , fIFD (ifd)
4361 , fBasic (basic)
4362 , fStream (stream)
4363 , fImage (image)
4364 , fFakeChannels (fakeChannels)
4365 , fTilesDown (tilesDown)
4366 , fTilesAcross (tilesAcross)
4367 , fCompressedSize (compressedSize)
4368 , fUncompressedSize (uncompressedSize)
4369 , fMutex1 ("dng_write_tiles_task_1")
4370 , fNextTileIndex (0)
4371 , fMutex2 ("dng_write_tiles_task_2")
4372 , fCondition ()
4373 , fTaskFailed (false)
4374 , fWriteTileIndex (0)
4375
4376 {
4377
4378 fMinTaskArea = 16 * 16;
4379 fUnitCell = dng_point (16, 16);
4380 fMaxTileSize = dng_point (16, 16);
4381
4382 }
4383
4384 void Process (uint32 /* threadIndex */,
4385 const dng_rect & /* tile */,
4386 dng_abort_sniffer *sniffer)
4387 {
4388
4389 try
4390 {
4391
4392 AutoPtr<dng_memory_block> compressedBuffer;
4393 AutoPtr<dng_memory_block> uncompressedBuffer;
4394 AutoPtr<dng_memory_block> subTileBlockBuffer;
4395 AutoPtr<dng_memory_block> tempBuffer;
4396
4397 if (fCompressedSize)
4398 {
4399 compressedBuffer.Reset (fHost.Allocate (fCompressedSize));
4400 }
4401
4402 if (fUncompressedSize)
4403 {
4404 uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize));
4405 }
4406
4407 if (fIFD.fSubTileBlockRows > 1 && fUncompressedSize)
4408 {
4409 subTileBlockBuffer.Reset (fHost.Allocate (fUncompressedSize));
4410 }
4411
4412 while (true)
4413 {
4414
4415 // Find tile index to compress.
4416
4417 uint32 tileIndex;
4418
4419 {
4420
4421 dng_lock_mutex lock (&fMutex1);
4422
4423 if (fNextTileIndex == fTilesDown * fTilesAcross)
4424 {
4425 return;
4426 }
4427
4428 tileIndex = fNextTileIndex++;
4429
4430 }
4431
4432 dng_abort_sniffer::SniffForAbort (sniffer);
4433
4434 // Compress tile.
4435
4436 uint32 rowIndex = tileIndex / fTilesAcross;
4437
4438 uint32 colIndex = tileIndex - rowIndex * fTilesAcross;
4439
4440 dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex);
4441
4442 dng_memory_stream tileStream (fHost.Allocator ());
4443
4444 tileStream.SetLittleEndian (fStream.LittleEndian ());
4445
4446 dng_host host (&fHost.Allocator (),
4447 sniffer);
4448
4449 fImageWriter.WriteTile (host,
4450 fIFD,
4451 tileStream,
4452 fImage,
4453 tileArea,
4454 fFakeChannels,
4455 compressedBuffer,
4456 uncompressedBuffer,
4457 subTileBlockBuffer,
4458 tempBuffer);
4459
4460 tileStream.Flush ();
4461
4462 uint32 tileByteCount = (uint32) tileStream.Length ();
4463
4464 tileStream.SetReadPosition (0);
4465
4466 // Wait until it is our turn to write tile.
4467
4468 {
4469
4470 dng_lock_mutex lock (&fMutex2);
4471
4472 while (!fTaskFailed &&
4473 fWriteTileIndex != tileIndex)
4474 {
4475
4476 fCondition.Wait (fMutex2);
4477
4478 }
4479
4480 // If the task failed in another thread, that thread already threw an exception.
4481
4482 if (fTaskFailed)
4483 return;
4484
4485 }
4486
4487 dng_abort_sniffer::SniffForAbort (sniffer);
4488
4489 // Remember this offset.
4490
4491 uint32 tileOffset = (uint32) fStream.Position ();
4492
4493 fBasic.SetTileOffset (tileIndex, tileOffset);
4494
4495 // Copy tile stream for tile into main stream.
4496
4497 tileStream.CopyToStream (fStream, tileByteCount);
4498
4499 // Update tile count.
4500
4501 fBasic.SetTileByteCount (tileIndex, tileByteCount);
4502
4503 // Keep the tiles on even byte offsets.
4504
4505 if (tileByteCount & 1)
4506 {
4507 fStream.Put_uint8 (0);
4508 }
4509
4510 // Let other threads know it is safe to write to stream.
4511
4512 {
4513
4514 dng_lock_mutex lock (&fMutex2);
4515
4516 // If the task failed in another thread, that thread already threw an exception.
4517
4518 if (fTaskFailed)
4519 return;
4520
4521 fWriteTileIndex++;
4522
4523 fCondition.Broadcast ();
4524
4525 }
4526
4527 }
4528
4529 }
4530
4531 catch (...)
4532 {
4533
4534 // If first to fail, wake up any threads waiting on condition.
4535
4536 bool needBroadcast = false;
4537
4538 {
4539
4540 dng_lock_mutex lock (&fMutex2);
4541
4542 needBroadcast = !fTaskFailed;
4543 fTaskFailed = true;
4544
4545 }
4546
4547 if (needBroadcast)
4548 fCondition.Broadcast ();
4549
4550 throw;
4551
4552 }
4553
4554 }
4555
4556 private:
4557
4558 // Hidden copy constructor and assignment operator.
4559
4560 dng_write_tiles_task (const dng_write_tiles_task &);
4561
4562 dng_write_tiles_task & operator= (const dng_write_tiles_task &);
4563
4564 };
4565
4566/*****************************************************************************/
4567
4568void dng_image_writer::WriteImage (dng_host &host,
4569 const dng_ifd &ifd,
4570 dng_basic_tag_set &basic,
4571 dng_stream &stream,
4572 const dng_image &image,
4573 uint32 fakeChannels)
4574 {
4575
4576 // Deal with row interleaved images.
4577
4578 if (ifd.fRowInterleaveFactor > 1 &&
4579 ifd.fRowInterleaveFactor < ifd.fImageLength)
4580 {
4581
4582 dng_ifd tempIFD (ifd);
4583
4584 tempIFD.fRowInterleaveFactor = 1;
4585
4586 dng_row_interleaved_image tempImage (*((dng_image *) &image),
4587 ifd.fRowInterleaveFactor);
4588
4589 WriteImage (host,
4590 tempIFD,
4591 basic,
4592 stream,
4593 tempImage,
4594 fakeChannels);
4595
4596 return;
4597
4598 }
4599
4600 // Compute basic information.
4601
4602 uint32 bytesPerSample = TagTypeSize (image.PixelType ());
4603
4604 uint32 bytesPerPixel = SafeUint32Mult (ifd.fSamplesPerPixel,
4605 bytesPerSample);
4606
4607 uint32 tileRowBytes = SafeUint32Mult (ifd.fTileWidth, bytesPerPixel);
4608
4609 // If we can compute the number of bytes needed to store the
4610 // data, we can split the write for each tile into sub-tiles.
4611
4612 uint32 subTileLength = ifd.fTileLength;
4613
4614 if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0)
4615 {
4616
4617 subTileLength = Pin_uint32 (ifd.fSubTileBlockRows,
4618 kImageBufferSize / tileRowBytes,
4619 ifd.fTileLength);
4620
4621 // Don't split sub-tiles across subTileBlocks.
4622
4623 subTileLength = subTileLength / ifd.fSubTileBlockRows
4624 * ifd.fSubTileBlockRows;
4625
4626 }
4627
4628 // Find size of uncompressed buffer.
4629
4630 uint32 uncompressedSize = SafeUint32Mult(subTileLength, tileRowBytes);
4631
4632 // Find size of compressed buffer, if required.
4633
4634 uint32 compressedSize = CompressedBufferSize (ifd, uncompressedSize);
4635
4636 // See if we can do this write using multiple threads.
4637
4638 uint32 tilesAcross = ifd.TilesAcross ();
4639 uint32 tilesDown = ifd.TilesDown ();
4640
4641 bool useMultipleThreads = (tilesDown * tilesAcross >= 2) &&
4642 (host.PerformAreaTaskThreads () > 1) &&
4643 (subTileLength == ifd.fTileLength) &&
4644 (ifd.fCompression != ccUncompressed);
4645
4646
4647#if qImagecore
4648 useMultipleThreads = false;
4649#endif
4650
4651 if (useMultipleThreads)
4652 {
4653
4654 uint32 threadCount = Min_uint32 (tilesDown * tilesAcross,
4655 host.PerformAreaTaskThreads ());
4656
4657 dng_write_tiles_task task (*this,
4658 host,
4659 ifd,
4660 basic,
4661 stream,
4662 image,
4663 fakeChannels,
4664 tilesDown,
4665 tilesAcross,
4666 compressedSize,
4667 uncompressedSize);
4668
4669 host.PerformAreaTask (task,
4670 dng_rect (0, 0, 16, 16 * threadCount));
4671
4672 }
4673
4674 else
4675 {
4676
4677 AutoPtr<dng_memory_block> compressedBuffer;
4678 AutoPtr<dng_memory_block> uncompressedBuffer;
4679 AutoPtr<dng_memory_block> subTileBlockBuffer;
4680 AutoPtr<dng_memory_block> tempBuffer;
4681
4682 if (compressedSize)
4683 {
4684 compressedBuffer.Reset (host.Allocate (compressedSize));
4685 }
4686
4687 if (uncompressedSize)
4688 {
4689 uncompressedBuffer.Reset (host.Allocate (uncompressedSize));
4690 }
4691
4692 if (ifd.fSubTileBlockRows > 1 && uncompressedSize)
4693 {
4694 subTileBlockBuffer.Reset (host.Allocate (uncompressedSize));
4695 }
4696
4697 // Write out each tile.
4698
4699 uint32 tileIndex = 0;
4700
4701 for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++)
4702 {
4703
4704 for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++)
4705 {
4706
4707 // Remember this offset.
4708
4709 uint32 tileOffset = (uint32) stream.Position ();
4710
4711 basic.SetTileOffset (tileIndex, tileOffset);
4712
4713 // Split tile into sub-tiles if possible.
4714
4715 dng_rect tileArea = ifd.TileArea (rowIndex, colIndex);
4716
4717 uint32 subTileCount = (tileArea.H () + subTileLength - 1) /
4718 subTileLength;
4719
4720 for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++)
4721 {
4722
4723 host.SniffForAbort ();
4724
4725 dng_rect subArea (tileArea);
4726
4727 subArea.t = tileArea.t + subIndex * subTileLength;
4728
4729 subArea.b = Min_int32 (subArea.t + subTileLength,
4730 tileArea.b);
4731
4732 // Write the sub-tile.
4733
4734 WriteTile (host,
4735 ifd,
4736 stream,
4737 image,
4738 subArea,
4739 fakeChannels,
4740 compressedBuffer,
4741 uncompressedBuffer,
4742 subTileBlockBuffer,
4743 tempBuffer);
4744
4745 }
4746
4747 // Update tile count.
4748
4749 uint32 tileByteCount = (uint32) stream.Position () - tileOffset;
4750
4751 basic.SetTileByteCount (tileIndex, tileByteCount);
4752
4753 tileIndex++;
4754
4755 // Keep the tiles on even byte offsets.
4756
4757 if (tileByteCount & 1)
4758 {
4759 stream.Put_uint8 (0);
4760 }
4761
4762 }
4763
4764 }
4765
4766 }
4767
4768 }
4769
4770/*****************************************************************************/
4771
4772#if qDNGUseXMP
4773
4774static void CopyString (const dng_xmp &oldXMP,
4775 dng_xmp &newXMP,
4776 const char *ns,
4777 const char *path,
4778 dng_string *exif = NULL)
4779 {
4780
4781 dng_string s;
4782
4783 if (oldXMP.GetString (ns, path, s))
4784 {
4785
4786 if (s.NotEmpty ())
4787 {
4788
4789 newXMP.SetString (ns, path, s);
4790
4791 if (exif)
4792 {
4793
4794 *exif = s;
4795
4796 }
4797
4798 }
4799
4800 }
4801
4802 }
4803
4804/*****************************************************************************/
4805
4806static void CopyStringList (const dng_xmp &oldXMP,
4807 dng_xmp &newXMP,
4808 const char *ns,
4809 const char *path,
4810 bool isBag)
4811 {
4812
4813 dng_string_list list;
4814
4815 if (oldXMP.GetStringList (ns, path, list))
4816 {
4817
4818 if (list.Count ())
4819 {
4820
4821 newXMP.SetStringList (ns, path, list, isBag);
4822
4823 }
4824
4825 }
4826
4827 }
4828
4829/*****************************************************************************/
4830
4831static void CopyAltLangDefault (const dng_xmp &oldXMP,
4832 dng_xmp &newXMP,
4833 const char *ns,
4834 const char *path,
4835 dng_string *exif = NULL)
4836 {
4837
4838 dng_string s;
4839
4840 if (oldXMP.GetAltLangDefault (ns, path, s))
4841 {
4842
4843 if (s.NotEmpty ())
4844 {
4845
4846 newXMP.SetAltLangDefault (ns, path, s);
4847
4848 if (exif)
4849 {
4850
4851 *exif = s;
4852
4853 }
4854
4855 }
4856
4857 }
4858
4859 }
4860
4861/*****************************************************************************/
4862
4863static void CopyStructField (const dng_xmp &oldXMP,
4864 dng_xmp &newXMP,
4865 const char *ns,
4866 const char *path,
4867 const char *field)
4868 {
4869
4870 dng_string s;
4871
4872 if (oldXMP.GetStructField (ns, path, ns, field, s))
4873 {
4874
4875 if (s.NotEmpty ())
4876 {
4877
4878 newXMP.SetStructField (ns, path, ns, field, s);
4879
4880 }
4881
4882 }
4883
4884 }
4885
4886/*****************************************************************************/
4887
4888static void CopyBoolean (const dng_xmp &oldXMP,
4889 dng_xmp &newXMP,
4890 const char *ns,
4891 const char *path)
4892 {
4893
4894 bool b;
4895
4896 if (oldXMP.GetBoolean (ns, path, b))
4897 {
4898
4899 newXMP.SetBoolean (ns, path, b);
4900
4901 }
4902
4903 }
4904
4905#endif
4906
4907/*****************************************************************************/
4908
4909void dng_image_writer::CleanUpMetadata (dng_host &host,
4910 dng_metadata &metadata,
4911 dng_metadata_subset metadataSubset,
4912 const char *dstMIMI,
4913 const char *software)
4914 {
4915
4916 #if qDNGUseXMP
4917
4918 if (metadata.GetXMP () && metadata.GetExif ())
4919 {
4920
4921 dng_xmp &newXMP (*metadata.GetXMP ());
4922 dng_exif &newEXIF (*metadata.GetExif ());
4923
4924 // Update software tag.
4925
4926 if (software)
4927 {
4928
4929 newEXIF.fSoftware.Set (software);
4930
4931 newXMP.Set (XMP_NS_XAP,
4932 "CreatorTool",
4933 software);
4934
4935 }
4936
4937 #if qDNGXMPDocOps
4938
4939 newXMP.DocOpsPrepareForSave (metadata.SourceMIMI ().Get (),
4940 dstMIMI);
4941
4942 #else
4943
4944 metadata.UpdateDateTimeToNow ();
4945
4946 #endif
4947
4948 // Update EXIF version to at least 2.3 so all the exif tags
4949 // can be written.
4950
4951 if (newEXIF.fExifVersion < DNG_CHAR4 ('0','2','3','0'))
4952 {
4953
4954 newEXIF.fExifVersion = DNG_CHAR4 ('0','2','3','0');
4955
4956 newXMP.Set (XMP_NS_EXIF, "ExifVersion", "0230");
4957
4958 }
4959
4960 // Resync EXIF, remove EXIF tags from XMP.
4961
4962 newXMP.SyncExif (newEXIF,
4963 metadata.GetOriginalExif (),
4964 false,
4965 true);
4966
4967 // Deal with ImageIngesterPro bug. This program is adding lots of
4968 // empty metadata strings into the XMP, which is screwing up Adobe CS4.
4969 // We are saving a new file, so this is a chance to clean up this mess.
4970
4971 newXMP.RemoveEmptyStringsAndArrays (XMP_NS_DC);
4972 newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP);
4973 newXMP.RemoveEmptyStringsAndArrays (XMP_NS_PHOTOSHOP);
4974 newXMP.RemoveEmptyStringsAndArrays (XMP_NS_IPTC);
4975 newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP_RIGHTS);
4976 newXMP.RemoveEmptyStringsAndArrays ("http://ns.iview-multimedia.com/mediapro/1.0/");
4977
4978 // Process metadata subset.
4979
4980 if (metadataSubset == kMetadataSubset_CopyrightOnly ||
4981 metadataSubset == kMetadataSubset_CopyrightAndContact)
4982 {
4983
4984 dng_xmp oldXMP (newXMP );
4985 dng_exif oldEXIF (newEXIF);
4986
4987 // For these options, we start from nothing, and only fill in the
4988 // fields that we absolutely need.
4989
4990 newXMP.RemoveProperties (NULL);
4991
4992 newEXIF.SetEmpty ();
4993
4994 metadata.ClearMakerNote ();
4995
4996 // Move copyright related fields over.
4997
4998 CopyAltLangDefault (oldXMP,
4999 newXMP,
5000 XMP_NS_DC,
5001 "rights",
5002 &newEXIF.fCopyright);
5003
5004 CopyAltLangDefault (oldXMP,
5005 newXMP,
5006 XMP_NS_XAP_RIGHTS,
5007 "UsageTerms");
5008
5009 CopyString (oldXMP,
5010 newXMP,
5011 XMP_NS_XAP_RIGHTS,
5012 "WebStatement");
5013
5014 CopyBoolean (oldXMP,
5015 newXMP,
5016 XMP_NS_XAP_RIGHTS,
5017 "Marked");
5018
5019 #if qDNGXMPDocOps
5020
5021 // Include basic DocOps fields, but not the full history.
5022
5023 CopyString (oldXMP,
5024 newXMP,
5025 XMP_NS_MM,
5026 "OriginalDocumentID");
5027
5028 CopyString (oldXMP,
5029 newXMP,
5030 XMP_NS_MM,
5031 "DocumentID");
5032
5033 CopyString (oldXMP,
5034 newXMP,
5035 XMP_NS_MM,
5036 "InstanceID");
5037
5038 CopyString (oldXMP,
5039 newXMP,
5040 XMP_NS_XAP,
5041 "MetadataDate");
5042
5043 #endif
5044
5045 // Copyright and Contact adds the contact info fields.
5046
5047 if (metadataSubset == kMetadataSubset_CopyrightAndContact)
5048 {
5049
5050 // Note: Save for Web is not including the dc:creator list, but it
5051 // is part of the IPTC contract info metadata panel, so I
5052 // think it should be copied as part of the contact info.
5053
5054 CopyStringList (oldXMP,
5055 newXMP,
5056 XMP_NS_DC,
5057 "creator",
5058 false);
5059
5060 // The first string dc:creator list is mirrored to the
5061 // the exif artist tag, so copy that also.
5062
5063 newEXIF.fArtist = oldEXIF.fArtist;
5064
5065 // Copy other contact fields.
5066
5067 CopyString (oldXMP,
5068 newXMP,
5069 XMP_NS_PHOTOSHOP,
5070 "AuthorsPosition");
5071
5072 CopyStructField (oldXMP,
5073 newXMP,
5074 XMP_NS_IPTC,
5075 "CreatorContactInfo",
5076 "CiEmailWork");
5077
5078 CopyStructField (oldXMP,
5079 newXMP,
5080 XMP_NS_IPTC,
5081 "CreatorContactInfo",
5082 "CiAdrExtadr");
5083
5084 CopyStructField (oldXMP,
5085 newXMP,
5086 XMP_NS_IPTC,
5087 "CreatorContactInfo",
5088 "CiAdrCity");
5089
5090 CopyStructField (oldXMP,
5091 newXMP,
5092 XMP_NS_IPTC,
5093 "CreatorContactInfo",
5094 "CiAdrRegion");
5095
5096 CopyStructField (oldXMP,
5097 newXMP,
5098 XMP_NS_IPTC,
5099 "CreatorContactInfo",
5100 "CiAdrPcode");
5101
5102 CopyStructField (oldXMP,
5103 newXMP,
5104 XMP_NS_IPTC,
5105 "CreatorContactInfo",
5106 "CiAdrCtry");
5107
5108 CopyStructField (oldXMP,
5109 newXMP,
5110 XMP_NS_IPTC,
5111 "CreatorContactInfo",
5112 "CiTelWork");
5113
5114 CopyStructField (oldXMP,
5115 newXMP,
5116 XMP_NS_IPTC,
5117 "CreatorContactInfo",
5118 "CiUrlWork");
5119
5120 CopyAltLangDefault (oldXMP,
5121 newXMP,
5122 XMP_NS_DC,
5123 "title");
5124
5125 }
5126
5127 }
5128
5129 else if (metadataSubset == kMetadataSubset_AllExceptCameraInfo ||
5130 metadataSubset == kMetadataSubset_AllExceptCameraAndLocation ||
5131 metadataSubset == kMetadataSubset_AllExceptLocationInfo)
5132 {
5133
5134 dng_xmp oldXMP (newXMP );
5135 dng_exif oldEXIF (newEXIF);
5136
5137 if (metadataSubset == kMetadataSubset_AllExceptCameraInfo ||
5138 metadataSubset == kMetadataSubset_AllExceptCameraAndLocation)
5139 {
5140
5141 // This removes most of the EXIF info, so just copy the fields
5142 // we are not deleting.
5143
5144 newEXIF.SetEmpty ();
5145
5146 newEXIF.fImageDescription = oldEXIF.fImageDescription; // Note: Differs from SFW
5147 newEXIF.fSoftware = oldEXIF.fSoftware;
5148 newEXIF.fArtist = oldEXIF.fArtist;
5149 newEXIF.fCopyright = oldEXIF.fCopyright;
5150 newEXIF.fCopyright2 = oldEXIF.fCopyright2;
5151 newEXIF.fDateTime = oldEXIF.fDateTime;
5152 newEXIF.fDateTimeOriginal = oldEXIF.fDateTimeOriginal;
5153 newEXIF.fDateTimeDigitized = oldEXIF.fDateTimeDigitized;
5154 newEXIF.fExifVersion = oldEXIF.fExifVersion;
5155 newEXIF.fImageUniqueID = oldEXIF.fImageUniqueID;
5156
5157 newEXIF.CopyGPSFrom (oldEXIF);
5158
5159 // Remove exif info from XMP.
5160
5161 newXMP.RemoveProperties (XMP_NS_EXIF);
5162 newXMP.RemoveProperties (XMP_NS_AUX);
5163
5164 // Remove Camera Raw info
5165
5166 newXMP.RemoveProperties (XMP_NS_CRS);
5167 newXMP.RemoveProperties (XMP_NS_CRSS);
5168 newXMP.RemoveProperties (XMP_NS_CRX);
5169
5170 // Remove DocOps history, since it contains the original
5171 // camera format.
5172
5173 newXMP.Remove (XMP_NS_MM, "History");
5174
5175 // MakerNote contains camera info.
5176
5177 metadata.ClearMakerNote ();
5178
5179 }
5180
5181 if (metadataSubset == kMetadataSubset_AllExceptLocationInfo ||
5182 metadataSubset == kMetadataSubset_AllExceptCameraAndLocation)
5183 {
5184
5185 // Remove GPS fields.
5186
5187 dng_exif blankExif;
5188
5189 newEXIF.CopyGPSFrom (blankExif);
5190
5191 // Remove MakerNote just in case, because we don't know
5192 // all of what is in it.
5193
5194 metadata.ClearMakerNote ();
5195
5196 // Remove XMP & IPTC location fields.
5197
5198 newXMP.Remove (XMP_NS_PHOTOSHOP, "City");
5199 newXMP.Remove (XMP_NS_PHOTOSHOP, "State");
5200 newXMP.Remove (XMP_NS_PHOTOSHOP, "Country");
5201 newXMP.Remove (XMP_NS_IPTC, "Location");
5202 newXMP.Remove (XMP_NS_IPTC, "CountryCode");
5203 newXMP.Remove (XMP_NS_IPTC_EXT, "LocationCreated");
5204 newXMP.Remove (XMP_NS_IPTC_EXT, "LocationShown");
5205
5206 }
5207
5208 }
5209
5210 // Rebuild the legacy IPTC block, if needed.
5211
5212 bool isTIFF = (strcmp (dstMIMI, "image/tiff") == 0);
5213 bool isDNG = (strcmp (dstMIMI, "image/dng" ) == 0);
5214
5215 if (!isDNG)
5216 {
5217
5218 metadata.RebuildIPTC (host.Allocator (),
5219 isTIFF);
5220
5221 }
5222
5223 else
5224 {
5225
5226 metadata.ClearIPTC ();
5227
5228 }
5229
5230 // Clear format related XMP.
5231
5232 newXMP.ClearOrientation ();
5233
5234 newXMP.ClearImageInfo ();
5235
5236 newXMP.RemoveProperties (XMP_NS_DNG);
5237
5238 // All the formats we care about already keep the IPTC digest
5239 // elsewhere, do we don't need to write it to the XMP.
5240
5241 newXMP.ClearIPTCDigest ();
5242
5243 // Make sure that sidecar specific tags never get written to files.
5244
5245 newXMP.Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension");
5246 newXMP.Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest");
5247
5248 }
5249
5250 #endif
5251
5252 }
5253
5254/*****************************************************************************/
5255
5256void dng_image_writer::WriteTIFF (dng_host &host,
5257 dng_stream &stream,
5258 const dng_image &image,
5259 uint32 photometricInterpretation,
5260 uint32 compression,
5261 dng_negative *negative,
5262 const dng_color_space *space,
5263 const dng_resolution *resolution,
5264 const dng_jpeg_preview *thumbnail,
5265 const dng_memory_block *imageResources,
5266 dng_metadata_subset metadataSubset)
5267 {
5268
5269 WriteTIFF (host,
5270 stream,
5271 image,
5272 photometricInterpretation,
5273 compression,
5274 negative ? &(negative->Metadata ()) : NULL,
5275 space,
5276 resolution,
5277 thumbnail,
5278 imageResources,
5279 metadataSubset);
5280
5281 }
5282
5283/*****************************************************************************/
5284
5285void dng_image_writer::WriteTIFF (dng_host &host,
5286 dng_stream &stream,
5287 const dng_image &image,
5288 uint32 photometricInterpretation,
5289 uint32 compression,
5290 const dng_metadata *metadata,
5291 const dng_color_space *space,
5292 const dng_resolution *resolution,
5293 const dng_jpeg_preview *thumbnail,
5294 const dng_memory_block *imageResources,
5295 dng_metadata_subset metadataSubset)
5296 {
5297
5298 const void *profileData = NULL;
5299 uint32 profileSize = 0;
5300
5301 const uint8 *data = NULL;
5302 uint32 size = 0;
5303
5304 if (space && space->ICCProfile (size, data))
5305 {
5306
5307 profileData = data;
5308 profileSize = size;
5309
5310 }
5311
5312 WriteTIFFWithProfile (host,
5313 stream,
5314 image,
5315 photometricInterpretation,
5316 compression,
5317 metadata,
5318 profileData,
5319 profileSize,
5320 resolution,
5321 thumbnail,
5322 imageResources,
5323 metadataSubset);
5324
5325 }
5326
5327/*****************************************************************************/
5328
5329void dng_image_writer::WriteTIFFWithProfile (dng_host &host,
5330 dng_stream &stream,
5331 const dng_image &image,
5332 uint32 photometricInterpretation,
5333 uint32 compression,
5334 dng_negative *negative,
5335 const void *profileData,
5336 uint32 profileSize,
5337 const dng_resolution *resolution,
5338 const dng_jpeg_preview *thumbnail,
5339 const dng_memory_block *imageResources,
5340 dng_metadata_subset metadataSubset)
5341 {
5342
5343 WriteTIFFWithProfile (host,
5344 stream,
5345 image,
5346 photometricInterpretation,
5347 compression,
5348 negative ? &(negative->Metadata ()) : NULL,
5349 profileData,
5350 profileSize,
5351 resolution,
5352 thumbnail,
5353 imageResources,
5354 metadataSubset);
5355
5356 }
5357
5358/*****************************************************************************/
5359
5360void dng_image_writer::WriteTIFFWithProfile (dng_host &host,
5361 dng_stream &stream,
5362 const dng_image &image,
5363 uint32 photometricInterpretation,
5364 uint32 compression,
5365 const dng_metadata *constMetadata,
5366 const void *profileData,
5367 uint32 profileSize,
5368 const dng_resolution *resolution,
5369 const dng_jpeg_preview *thumbnail,
5370 const dng_memory_block *imageResources,
5371 dng_metadata_subset metadataSubset)
5372 {
5373
5374 uint32 j;
5375
5376 AutoPtr<dng_metadata> metadata;
5377
5378 if (constMetadata)
5379 {
5380
5381 metadata.Reset (constMetadata->Clone (host.Allocator ()));
5382
5383 CleanUpMetadata (host,
5384 *metadata,
5385 metadataSubset,
5386 "image/tiff");
5387
5388 }
5389
5390 dng_ifd ifd;
5391
5392 ifd.fNewSubFileType = sfMainImage;
5393
5394 ifd.fImageWidth = image.Bounds ().W ();
5395 ifd.fImageLength = image.Bounds ().H ();
5396
5397 ifd.fSamplesPerPixel = image.Planes ();
5398
5399 ifd.fBitsPerSample [0] = TagTypeSize (image.PixelType ()) * 8;
5400
5401 for (j = 1; j < ifd.fSamplesPerPixel; j++)
5402 {
5403 ifd.fBitsPerSample [j] = ifd.fBitsPerSample [0];
5404 }
5405
5406 ifd.fPhotometricInterpretation = photometricInterpretation;
5407
5408 ifd.fCompression = compression;
5409
5410 if (ifd.fCompression == ccUncompressed)
5411 {
5412
5413 ifd.SetSingleStrip ();
5414
5415 }
5416
5417 else
5418 {
5419
5420 ifd.FindStripSize (128 * 1024);
5421
5422 ifd.fPredictor = cpHorizontalDifference;
5423
5424 }
5425
5426 uint32 extraSamples = 0;
5427
5428 switch (photometricInterpretation)
5429 {
5430
5431 case piBlackIsZero:
5432 {
5433 extraSamples = image.Planes () - 1;
5434 break;
5435 }
5436
5437 case piRGB:
5438 {
5439 extraSamples = image.Planes () - 3;
5440 break;
5441 }
5442
5443 default:
5444 break;
5445
5446 }
5447
5448 ifd.fExtraSamplesCount = extraSamples;
5449
5450 if (image.PixelType () == ttFloat)
5451 {
5452
5453 for (j = 0; j < ifd.fSamplesPerPixel; j++)
5454 {
5455 ifd.fSampleFormat [j] = sfFloatingPoint;
5456 }
5457
5458 }
5459
5460 dng_tiff_directory mainIFD;
5461
5462 dng_basic_tag_set basic (mainIFD, ifd);
5463
5464 // Resolution.
5465
5466 dng_resolution res;
5467
5468 if (resolution)
5469 {
5470 res = *resolution;
5471 }
5472
5473 tag_urational tagXResolution (tcXResolution, res.fXResolution);
5474 tag_urational tagYResolution (tcYResolution, res.fYResolution);
5475
5476 tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit);
5477
5478 if (resolution)
5479 {
5480 mainIFD.Add (&tagXResolution );
5481 mainIFD.Add (&tagYResolution );
5482 mainIFD.Add (&tagResolutionUnit);
5483 }
5484
5485 // ICC Profile.
5486
5487 tag_icc_profile iccProfileTag (profileData, profileSize);
5488
5489 if (iccProfileTag.Count ())
5490 {
5491 mainIFD.Add (&iccProfileTag);
5492 }
5493
5494 // XMP metadata.
5495
5496 #if qDNGUseXMP
5497
5498 tag_xmp tagXMP (metadata.Get () ? metadata->GetXMP () : NULL);
5499
5500 if (tagXMP.Count ())
5501 {
5502 mainIFD.Add (&tagXMP);
5503 }
5504
5505 #endif
5506
5507 // IPTC metadata.
5508
5509 tag_iptc tagIPTC (metadata.Get () ? metadata->IPTCData () : NULL,
5510 metadata.Get () ? metadata->IPTCLength () : 0);
5511
5512 if (tagIPTC.Count ())
5513 {
5514 mainIFD.Add (&tagIPTC);
5515 }
5516
5517 // Adobe data (thumbnail and IPTC digest)
5518
5519 AutoPtr<dng_memory_block> adobeData (BuildAdobeData (host,
5520 metadata.Get (),
5521 thumbnail,
5522 imageResources));
5523
5524 tag_uint8_ptr tagAdobe (tcAdobeData,
5525 adobeData->Buffer_uint8 (),
5526 adobeData->LogicalSize ());
5527
5528 if (tagAdobe.Count ())
5529 {
5530 mainIFD.Add (&tagAdobe);
5531 }
5532
5533 // Exif metadata.
5534
5535 exif_tag_set exifSet (mainIFD,
5536 metadata.Get () && metadata->GetExif () ? *metadata->GetExif ()
5537 : dng_exif (),
5538 metadata.Get () ? metadata->IsMakerNoteSafe () : false,
5539 metadata.Get () ? metadata->MakerNoteData () : NULL,
5540 metadata.Get () ? metadata->MakerNoteLength () : 0,
5541 false);
5542
5543 // Find offset to main image data.
5544
5545 uint32 offsetMainIFD = 8;
5546
5547 uint32 offsetExifData = offsetMainIFD + mainIFD.Size ();
5548
5549 exifSet.Locate (offsetExifData);
5550
5551 uint32 offsetMainData = offsetExifData + exifSet.Size ();
5552
5553 stream.SetWritePosition (offsetMainData);
5554
5555 // Write the main image data.
5556
5557 WriteImage (host,
5558 ifd,
5559 basic,
5560 stream,
5561 image);
5562
5563 // Trim the file to this length.
5564
5565 stream.SetLength (stream.Position ());
5566
5567 // TIFF has a 4G size limit.
5568
5569 if (stream.Length () > 0x0FFFFFFFFL)
5570 {
5571 ThrowImageTooBigTIFF ();
5572 }
5573
5574 // Write TIFF Header.
5575
5576 stream.SetWritePosition (0);
5577
5578 stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII);
5579
5580 stream.Put_uint16 (42);
5581
5582 stream.Put_uint32 (offsetMainIFD);
5583
5584 // Write the IFDs.
5585
5586 mainIFD.Put (stream);
5587
5588 exifSet.Put (stream);
5589
5590 stream.Flush ();
5591
5592 }
5593
5594/*****************************************************************************/
5595
5596void dng_image_writer::WriteDNG (dng_host &host,
5597 dng_stream &stream,
5598 dng_negative &negative,
5599 const dng_preview_list *previewList,
5600 uint32 maxBackwardVersion,
5601 bool uncompressed)
5602 {
5603
5604 WriteDNG (host,
5605 stream,
5606 negative,
5607 negative.Metadata (),
5608 previewList,
5609 maxBackwardVersion,
5610 uncompressed);
5611
5612 }
5613
5614/*****************************************************************************/
5615
5616void dng_image_writer::WriteDNG (dng_host &host,
5617 dng_stream &stream,
5618 const dng_negative &negative,
5619 const dng_metadata &constMetadata,
5620 const dng_preview_list *previewList,
5621 uint32 maxBackwardVersion,
5622 bool uncompressed)
5623 {
5624
5625 uint32 j;
5626
5627 // Clean up metadata per MWG recommendations.
5628
5629 AutoPtr<dng_metadata> metadata (constMetadata.Clone (host.Allocator ()));
5630
5631 CleanUpMetadata (host,
5632 *metadata,
5633 kMetadataSubset_All,
5634 "image/dng");
5635
5636 // Figure out the compression to use. Most of the time this is lossless
5637 // JPEG.
5638
5639 uint32 compression = uncompressed ? ccUncompressed : ccJPEG;
5640
5641 // Was the the original file lossy JPEG compressed?
5642
5643 const dng_jpeg_image *rawJPEGImage = negative.RawJPEGImage ();
5644
5645 // If so, can we save it using the requested compression and DNG version?
5646
5647 if (uncompressed || maxBackwardVersion < dngVersion_1_4_0_0)
5648 {
5649
5650 if (rawJPEGImage || negative.RawJPEGImageDigest ().IsValid ())
5651 {
5652
5653 rawJPEGImage = NULL;
5654
5655 negative.ClearRawJPEGImageDigest ();
5656
5657 negative.ClearRawImageDigest ();
5658
5659 }
5660
5661 }
5662
5663 else if (rawJPEGImage)
5664 {
5665
5666 compression = ccLossyJPEG;
5667
5668 }
5669
5670 // Are we saving the original size tags?
5671
5672 bool saveOriginalDefaultFinalSize = false;
5673 bool saveOriginalBestQualityFinalSize = false;
5674 bool saveOriginalDefaultCropSize = false;
5675
5676 {
5677
5678 // See if we are saving a proxy image.
5679
5680 dng_point defaultFinalSize (negative.DefaultFinalHeight (),
5681 negative.DefaultFinalWidth ());
5682
5683 saveOriginalDefaultFinalSize = (negative.OriginalDefaultFinalSize () !=
5684 defaultFinalSize);
5685
5686 if (saveOriginalDefaultFinalSize)
5687 {
5688
5689 // If the save OriginalDefaultFinalSize tag, this changes the defaults
5690 // for the OriginalBestQualityFinalSize and OriginalDefaultCropSize tags.
5691
5692 saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () !=
5693 defaultFinalSize);
5694
5695 saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () !=
5696 dng_urational (defaultFinalSize.v, 1)) ||
5697 (negative.OriginalDefaultCropSizeH () !=
5698 dng_urational (defaultFinalSize.h, 1));
5699
5700 }
5701
5702 else
5703 {
5704
5705 // Else these two tags default to the normal non-proxy size image values.
5706
5707 dng_point bestQualityFinalSize (negative.BestQualityFinalHeight (),
5708 negative.BestQualityFinalWidth ());
5709
5710 saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () !=
5711 bestQualityFinalSize);
5712
5713 saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () !=
5714 negative.DefaultCropSizeV ()) ||
5715 (negative.OriginalDefaultCropSizeH () !=
5716 negative.DefaultCropSizeH ());
5717
5718 }
5719
5720 }
5721
5722 // Is this a floating point image that we are saving?
5723
5724 bool isFloatingPoint = (negative.RawImage ().PixelType () == ttFloat);
5725
5726 // Does this image have a transparency mask?
5727
5728 bool hasTransparencyMask = (negative.RawTransparencyMask () != NULL);
5729
5730 // Should we save a compressed 32-bit integer file?
5731
5732 bool isCompressed32BitInteger = (negative.RawImage ().PixelType () == ttLong) &&
5733 (maxBackwardVersion >= dngVersion_1_4_0_0) &&
5734 (!uncompressed);
5735
5736 // Figure out what main version to use.
5737
5738 uint32 dngVersion = dngVersion_Current;
5739
5740 // Don't write version 1.4 files unless we actually use some feature of the 1.4 spec.
5741
5742 if (dngVersion == dngVersion_1_4_0_0)
5743 {
5744
5745 if (!rawJPEGImage &&
5746 !isFloatingPoint &&
5747 !hasTransparencyMask &&
5748 !isCompressed32BitInteger &&
5749 !saveOriginalDefaultFinalSize &&
5750 !saveOriginalBestQualityFinalSize &&
5751 !saveOriginalDefaultCropSize )
5752 {
5753
5754 dngVersion = dngVersion_1_3_0_0;
5755
5756 }
5757
5758 }
5759
5760 // Figure out what backward version to use.
5761
5762 uint32 dngBackwardVersion = dngVersion_1_1_0_0;
5763
5764 #if defined(qTestRowInterleave) || defined(qTestSubTileBlockRows) || defined(qTestSubTileBlockCols)
5765 dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_2_0_0);
5766 #endif
5767
5768 dngBackwardVersion = Max_uint32 (dngBackwardVersion,
5769 negative.OpcodeList1 ().MinVersion (false));
5770
5771 dngBackwardVersion = Max_uint32 (dngBackwardVersion,
5772 negative.OpcodeList2 ().MinVersion (false));
5773
5774 dngBackwardVersion = Max_uint32 (dngBackwardVersion,
5775 negative.OpcodeList3 ().MinVersion (false));
5776
5777 if (negative.GetMosaicInfo () &&
5778 negative.GetMosaicInfo ()->fCFALayout >= 6)
5779 {
5780 dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_3_0_0);
5781 }
5782
5783 if (rawJPEGImage || isFloatingPoint || hasTransparencyMask || isCompressed32BitInteger)
5784 {
5785 dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_4_0_0);
5786 }
5787
5788 if (dngBackwardVersion > dngVersion)
5789 {
5790 ThrowProgramError ();
5791 }
5792
5793 // Find best thumbnail from preview list, if any.
5794
5795 const dng_preview *thumbnail = NULL;
5796
5797 if (previewList)
5798 {
5799
5800 uint32 thumbArea = 0;
5801
5802 for (j = 0; j < previewList->Count (); j++)
5803 {
5804
5805 const dng_image_preview *imagePreview = dynamic_cast<const dng_image_preview *>(&previewList->Preview (j));
5806
5807 if (imagePreview)
5808 {
5809
5810 uint32 thisArea = imagePreview->fImage->Bounds ().W () *
5811 imagePreview->fImage->Bounds ().H ();
5812
5813 if (!thumbnail || thisArea < thumbArea)
5814 {
5815
5816 thumbnail = &previewList->Preview (j);
5817
5818 thumbArea = thisArea;
5819
5820 }
5821
5822 }
5823
5824 const dng_jpeg_preview *jpegPreview = dynamic_cast<const dng_jpeg_preview *>(&previewList->Preview (j));
5825
5826 if (jpegPreview)
5827 {
5828
5829 uint32 thisArea = jpegPreview->fPreviewSize.h *
5830 jpegPreview->fPreviewSize.v;
5831
5832 if (!thumbnail || thisArea < thumbArea)
5833 {
5834
5835 thumbnail = &previewList->Preview (j);
5836
5837 thumbArea = thisArea;
5838
5839 }
5840
5841 }
5842
5843 }
5844
5845 }
5846
5847 // Create the main IFD
5848
5849 dng_tiff_directory mainIFD;
5850
5851 // Create the IFD for the raw data. If there is no thumnail, this is
5852 // just a reference the main IFD. Otherwise allocate a new one.
5853
5854 AutoPtr<dng_tiff_directory> rawIFD_IfNotMain;
5855
5856 if (thumbnail)
5857 {
5858 rawIFD_IfNotMain.Reset (new dng_tiff_directory);
5859 }
5860
5861 dng_tiff_directory &rawIFD (thumbnail ? *rawIFD_IfNotMain : mainIFD);
5862
5863 // Include DNG version tags.
5864
5865 uint8 dngVersionData [4];
5866
5867 dngVersionData [0] = (uint8) (dngVersion >> 24);
5868 dngVersionData [1] = (uint8) (dngVersion >> 16);
5869 dngVersionData [2] = (uint8) (dngVersion >> 8);
5870 dngVersionData [3] = (uint8) (dngVersion );
5871
5872 tag_uint8_ptr tagDNGVersion (tcDNGVersion, dngVersionData, 4);
5873
5874 mainIFD.Add (&tagDNGVersion);
5875
5876 uint8 dngBackwardVersionData [4];
5877
5878 dngBackwardVersionData [0] = (uint8) (dngBackwardVersion >> 24);
5879 dngBackwardVersionData [1] = (uint8) (dngBackwardVersion >> 16);
5880 dngBackwardVersionData [2] = (uint8) (dngBackwardVersion >> 8);
5881 dngBackwardVersionData [3] = (uint8) (dngBackwardVersion );
5882
5883 tag_uint8_ptr tagDNGBackwardVersion (tcDNGBackwardVersion, dngBackwardVersionData, 4);
5884
5885 mainIFD.Add (&tagDNGBackwardVersion);
5886
5887 // The main IFD contains the thumbnail, if there is a thumbnail.
5888
5889 AutoPtr<dng_basic_tag_set> thmBasic;
5890
5891 if (thumbnail)
5892 {
5893 thmBasic.Reset (thumbnail->AddTagSet (mainIFD));
5894 }
5895
5896 // Get the raw image we are writing.
5897
5898 const dng_image &rawImage (negative.RawImage ());
5899
5900 // For floating point, we only support ZIP compression.
5901
5902 if (isFloatingPoint && !uncompressed)
5903 {
5904
5905 compression = ccDeflate;
5906
5907 }
5908
5909 // For 32-bit integer images, we only support ZIP and uncompressed.
5910
5911 if (rawImage.PixelType () == ttLong)
5912 {
5913
5914 if (isCompressed32BitInteger)
5915 {
5916 compression = ccDeflate;
5917 }
5918
5919 else
5920 {
5921 compression = ccUncompressed;
5922 }
5923
5924 }
5925
5926 // Get a copy of the mosaic info.
5927
5928 dng_mosaic_info mosaicInfo;
5929
5930 if (negative.GetMosaicInfo ())
5931 {
5932 mosaicInfo = *(negative.GetMosaicInfo ());
5933 }
5934
5935 // Create a dng_ifd record for the raw image.
5936
5937 dng_ifd info;
5938
5939 info.fImageWidth = rawImage.Width ();
5940 info.fImageLength = rawImage.Height ();
5941
5942 info.fSamplesPerPixel = rawImage.Planes ();
5943
5944 info.fPhotometricInterpretation = mosaicInfo.IsColorFilterArray () ? piCFA
5945 : piLinearRaw;
5946
5947 info.fCompression = compression;
5948
5949 if (isFloatingPoint && compression == ccDeflate)
5950 {
5951
5952 info.fPredictor = cpFloatingPoint;
5953
5954 if (mosaicInfo.IsColorFilterArray ())
5955 {
5956
5957 if (mosaicInfo.fCFAPatternSize.h == 2)
5958 {
5959 info.fPredictor = cpFloatingPointX2;
5960 }
5961
5962 else if (mosaicInfo.fCFAPatternSize.h == 4)
5963 {
5964 info.fPredictor = cpFloatingPointX4;
5965 }
5966
5967 }
5968
5969 }
5970
5971 if (isCompressed32BitInteger)
5972 {
5973
5974 info.fPredictor = cpHorizontalDifference;
5975
5976 if (mosaicInfo.IsColorFilterArray ())
5977 {
5978
5979 if (mosaicInfo.fCFAPatternSize.h == 2)
5980 {
5981 info.fPredictor = cpHorizontalDifferenceX2;
5982 }
5983
5984 else if (mosaicInfo.fCFAPatternSize.h == 4)
5985 {
5986 info.fPredictor = cpHorizontalDifferenceX4;
5987 }
5988
5989 }
5990
5991 }
5992
5993 uint32 rawPixelType = rawImage.PixelType ();
5994
5995 if (rawPixelType == ttShort)
5996 {
5997
5998 // See if we are using a linearization table with <= 256 entries, in which
5999 // case the useful data will all fit within 8-bits.
6000
6001 const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo ();
6002
6003 if (rangeInfo)
6004 {
6005
6006 if (rangeInfo->fLinearizationTable.Get ())
6007 {
6008
6009 uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1;
6010
6011 if (entries <= 256)
6012 {
6013
6014 rawPixelType = ttByte;
6015
6016 }
6017
6018 }
6019
6020 }
6021
6022 }
6023
6024 switch (rawPixelType)
6025 {
6026
6027 case ttByte:
6028 {
6029 info.fBitsPerSample [0] = 8;
6030 break;
6031 }
6032
6033 case ttShort:
6034 {
6035 info.fBitsPerSample [0] = 16;
6036 break;
6037 }
6038
6039 case ttLong:
6040 {
6041 info.fBitsPerSample [0] = 32;
6042 break;
6043 }
6044
6045 case ttFloat:
6046 {
6047
6048 if (negative.RawFloatBitDepth () == 16)
6049 {
6050 info.fBitsPerSample [0] = 16;
6051 }
6052
6053 else if (negative.RawFloatBitDepth () == 24)
6054 {
6055 info.fBitsPerSample [0] = 24;
6056 }
6057
6058 else
6059 {
6060 info.fBitsPerSample [0] = 32;
6061 }
6062
6063 for (j = 0; j < info.fSamplesPerPixel; j++)
6064 {
6065 info.fSampleFormat [j] = sfFloatingPoint;
6066 }
6067
6068 break;
6069
6070 }
6071
6072 default:
6073 {
6074 ThrowProgramError ();
6075 }
6076
6077 }
6078
6079 // For lossless JPEG compression, we often lie about the
6080 // actual channel count to get the predictors to work across
6081 // same color mosaic pixels.
6082
6083 uint32 fakeChannels = 1;
6084
6085 if (info.fCompression == ccJPEG)
6086 {
6087
6088 if (mosaicInfo.IsColorFilterArray ())
6089 {
6090
6091 if (mosaicInfo.fCFAPatternSize.h == 4)
6092 {
6093 fakeChannels = 4;
6094 }
6095
6096 else if (mosaicInfo.fCFAPatternSize.h == 2)
6097 {
6098 fakeChannels = 2;
6099 }
6100
6101 // However, lossless JEPG is limited to four channels,
6102 // so compromise might be required.
6103
6104 while (fakeChannels * info.fSamplesPerPixel > 4 &&
6105 fakeChannels > 1)
6106 {
6107
6108 fakeChannels >>= 1;
6109
6110 }
6111
6112 }
6113
6114 }
6115
6116 // Figure out tile sizes.
6117
6118 if (rawJPEGImage)
6119 {
6120
6121 DNG_ASSERT (rawPixelType == ttByte,
6122 "Unexpected jpeg pixel type");
6123
6124 DNG_ASSERT (info.fImageWidth == (uint32) rawJPEGImage->fImageSize.h &&
6125 info.fImageLength == (uint32) rawJPEGImage->fImageSize.v,
6126 "Unexpected jpeg image size");
6127
6128 info.fTileWidth = rawJPEGImage->fTileSize.h;
6129 info.fTileLength = rawJPEGImage->fTileSize.v;
6130
6131 info.fUsesStrips = rawJPEGImage->fUsesStrips;
6132
6133 info.fUsesTiles = !info.fUsesStrips;
6134
6135 }
6136
6137 else if (info.fCompression == ccJPEG)
6138 {
6139
6140 info.FindTileSize (128 * 1024);
6141
6142 }
6143
6144 else if (info.fCompression == ccDeflate)
6145 {
6146
6147 info.FindTileSize (512 * 1024);
6148
6149 }
6150
6151 else if (info.fCompression == ccLossyJPEG)
6152 {
6153
6154 ThrowProgramError ("No JPEG compressed image");
6155
6156 }
6157
6158 // Don't use tiles for uncompressed images.
6159
6160 else
6161 {
6162
6163 info.SetSingleStrip ();
6164
6165 }
6166
6167 #ifdef qTestRowInterleave
6168
6169 info.fRowInterleaveFactor = qTestRowInterleave;
6170
6171 #endif
6172
6173 #if defined(qTestSubTileBlockRows) && defined(qTestSubTileBlockCols)
6174
6175 info.fSubTileBlockRows = qTestSubTileBlockRows;
6176 info.fSubTileBlockCols = qTestSubTileBlockCols;
6177
6178 if (fakeChannels == 2)
6179 fakeChannels = 4;
6180
6181 #endif
6182
6183 // Basic information.
6184
6185 dng_basic_tag_set rawBasic (rawIFD, info);
6186
6187 // JPEG tables, if any.
6188
6189 tag_data_ptr tagJPEGTables (tcJPEGTables,
6190 ttUndefined,
6191 0,
6192 NULL);
6193
6194 if (rawJPEGImage && rawJPEGImage->fJPEGTables.Get ())
6195 {
6196
6197 tagJPEGTables.SetData (rawJPEGImage->fJPEGTables->Buffer ());
6198
6199 tagJPEGTables.SetCount (rawJPEGImage->fJPEGTables->LogicalSize ());
6200
6201 rawIFD.Add (&tagJPEGTables);
6202
6203 }
6204
6205 // DefaultScale tag.
6206
6207 dng_urational defaultScaleData [2];
6208
6209 defaultScaleData [0] = negative.DefaultScaleH ();
6210 defaultScaleData [1] = negative.DefaultScaleV ();
6211
6212 tag_urational_ptr tagDefaultScale (tcDefaultScale,
6213 defaultScaleData,
6214 2);
6215
6216 rawIFD.Add (&tagDefaultScale);
6217
6218 // Best quality scale tag.
6219
6220 tag_urational tagBestQualityScale (tcBestQualityScale,
6221 negative.BestQualityScale ());
6222
6223 rawIFD.Add (&tagBestQualityScale);
6224
6225 // DefaultCropOrigin tag.
6226
6227 dng_urational defaultCropOriginData [2];
6228
6229 defaultCropOriginData [0] = negative.DefaultCropOriginH ();
6230 defaultCropOriginData [1] = negative.DefaultCropOriginV ();
6231
6232 tag_urational_ptr tagDefaultCropOrigin (tcDefaultCropOrigin,
6233 defaultCropOriginData,
6234 2);
6235
6236 rawIFD.Add (&tagDefaultCropOrigin);
6237
6238 // DefaultCropSize tag.
6239
6240 dng_urational defaultCropSizeData [2];
6241
6242 defaultCropSizeData [0] = negative.DefaultCropSizeH ();
6243 defaultCropSizeData [1] = negative.DefaultCropSizeV ();
6244
6245 tag_urational_ptr tagDefaultCropSize (tcDefaultCropSize,
6246 defaultCropSizeData,
6247 2);
6248
6249 rawIFD.Add (&tagDefaultCropSize);
6250
6251 // DefaultUserCrop tag.
6252
6253 dng_urational defaultUserCropData [4];
6254
6255 defaultUserCropData [0] = negative.DefaultUserCropT ();
6256 defaultUserCropData [1] = negative.DefaultUserCropL ();
6257 defaultUserCropData [2] = negative.DefaultUserCropB ();
6258 defaultUserCropData [3] = negative.DefaultUserCropR ();
6259
6260 tag_urational_ptr tagDefaultUserCrop (tcDefaultUserCrop,
6261 defaultUserCropData,
6262 4);
6263
6264 rawIFD.Add (&tagDefaultUserCrop);
6265
6266 // Range mapping tag set.
6267
6268 range_tag_set rangeSet (rawIFD, negative);
6269
6270 // Mosaic pattern information.
6271
6272 mosaic_tag_set mosaicSet (rawIFD, mosaicInfo);
6273
6274 // Chroma blur radius.
6275
6276 tag_urational tagChromaBlurRadius (tcChromaBlurRadius,
6277 negative.ChromaBlurRadius ());
6278
6279 if (negative.ChromaBlurRadius ().IsValid ())
6280 {
6281
6282 rawIFD.Add (&tagChromaBlurRadius);
6283
6284 }
6285
6286 // Anti-alias filter strength.
6287
6288 tag_urational tagAntiAliasStrength (tcAntiAliasStrength,
6289 negative.AntiAliasStrength ());
6290
6291 if (negative.AntiAliasStrength ().IsValid ())
6292 {
6293
6294 rawIFD.Add (&tagAntiAliasStrength);
6295
6296 }
6297
6298 // Profile and other color related tags.
6299
6300 AutoPtr<profile_tag_set> profileSet;
6301
6302 AutoPtr<color_tag_set> colorSet;
6303
6304 dng_std_vector<uint32> extraProfileIndex;
6305
6306 if (!negative.IsMonochrome ())
6307 {
6308
6309 const dng_camera_profile &mainProfile (*negative.ComputeCameraProfileToEmbed (constMetadata));
6310
6311 profileSet.Reset (new profile_tag_set (mainIFD,
6312 mainProfile));
6313
6314 colorSet.Reset (new color_tag_set (mainIFD,
6315 negative));
6316
6317 // Build list of profile indices to include in extra profiles tag.
6318
6319 uint32 profileCount = negative.ProfileCount ();
6320
6321 for (uint32 index = 0; index < profileCount; index++)
6322 {
6323
6324 const dng_camera_profile &profile (negative.ProfileByIndex (index));
6325
6326 if (&profile != &mainProfile)
6327 {
6328
6329 if (profile.WasReadFromDNG ())
6330 {
6331
6332 extraProfileIndex.push_back (index);
6333
6334 }
6335
6336 }
6337
6338 }
6339
6340 }
6341
6342 // Extra camera profiles tag.
6343
6344 uint32 extraProfileCount = (uint32) extraProfileIndex.size ();
6345
6346 dng_memory_data extraProfileOffsets (extraProfileCount, sizeof (uint32));
6347
6348 tag_uint32_ptr extraProfileTag (tcExtraCameraProfiles,
6349 extraProfileOffsets.Buffer_uint32 (),
6350 extraProfileCount);
6351
6352 if (extraProfileCount)
6353 {
6354
6355 mainIFD.Add (&extraProfileTag);
6356
6357 }
6358
6359 // Other tags.
6360
6361 tag_uint16 tagOrientation (tcOrientation,
6362 (uint16) negative.ComputeOrientation (constMetadata).GetTIFF ());
6363
6364 mainIFD.Add (&tagOrientation);
6365
6366 tag_srational tagBaselineExposure (tcBaselineExposure,
6367 negative.BaselineExposureR ());
6368
6369 mainIFD.Add (&tagBaselineExposure);
6370
6371 tag_urational tagBaselineNoise (tcBaselineNoise,
6372 negative.BaselineNoiseR ());
6373
6374 mainIFD.Add (&tagBaselineNoise);
6375
6376 tag_urational tagNoiseReductionApplied (tcNoiseReductionApplied,
6377 negative.NoiseReductionApplied ());
6378
6379 if (negative.NoiseReductionApplied ().IsValid ())
6380 {
6381
6382 mainIFD.Add (&tagNoiseReductionApplied);
6383
6384 }
6385
6386 tag_dng_noise_profile tagNoiseProfile (negative.NoiseProfile ());
6387
6388 if (negative.NoiseProfile ().IsValidForNegative (negative))
6389 {
6390
6391 mainIFD.Add (&tagNoiseProfile);
6392
6393 }
6394
6395 tag_urational tagBaselineSharpness (tcBaselineSharpness,
6396 negative.BaselineSharpnessR ());
6397
6398 mainIFD.Add (&tagBaselineSharpness);
6399
6400 tag_string tagUniqueName (tcUniqueCameraModel,
6401 negative.ModelName (),
6402 true);
6403
6404 mainIFD.Add (&tagUniqueName);
6405
6406 tag_string tagLocalName (tcLocalizedCameraModel,
6407 negative.LocalName (),
6408 false);
6409
6410 if (negative.LocalName ().NotEmpty ())
6411 {
6412
6413 mainIFD.Add (&tagLocalName);
6414
6415 }
6416
6417 tag_urational tagShadowScale (tcShadowScale,
6418 negative.ShadowScaleR ());
6419
6420 mainIFD.Add (&tagShadowScale);
6421
6422 tag_uint16 tagColorimetricReference (tcColorimetricReference,
6423 (uint16) negative.ColorimetricReference ());
6424
6425 if (negative.ColorimetricReference () != crSceneReferred)
6426 {
6427
6428 mainIFD.Add (&tagColorimetricReference);
6429
6430 }
6431
6432 bool useNewDigest = (maxBackwardVersion >= dngVersion_1_4_0_0);
6433
6434 if (compression == ccLossyJPEG)
6435 {
6436
6437 negative.FindRawJPEGImageDigest (host);
6438
6439 }
6440
6441 else
6442 {
6443
6444 if (useNewDigest)
6445 {
6446 negative.FindNewRawImageDigest (host);
6447 }
6448 else
6449 {
6450 negative.FindRawImageDigest (host);
6451 }
6452
6453 }
6454
6455 tag_uint8_ptr tagRawImageDigest (useNewDigest ? tcNewRawImageDigest : tcRawImageDigest,
6456 compression == ccLossyJPEG ?
6457 negative.RawJPEGImageDigest ().data :
6458 (useNewDigest ? negative.NewRawImageDigest ().data
6459 : negative.RawImageDigest ().data),
6460 16);
6461
6462 mainIFD.Add (&tagRawImageDigest);
6463
6464 negative.FindRawDataUniqueID (host);
6465
6466 tag_uint8_ptr tagRawDataUniqueID (tcRawDataUniqueID,
6467 negative.RawDataUniqueID ().data,
6468 16);
6469
6470 if (negative.RawDataUniqueID ().IsValid ())
6471 {
6472
6473 mainIFD.Add (&tagRawDataUniqueID);
6474
6475 }
6476
6477 tag_string tagOriginalRawFileName (tcOriginalRawFileName,
6478 negative.OriginalRawFileName (),
6479 false);
6480
6481 if (negative.HasOriginalRawFileName ())
6482 {
6483
6484 mainIFD.Add (&tagOriginalRawFileName);
6485
6486 }
6487
6488 negative.FindOriginalRawFileDigest ();
6489
6490 tag_data_ptr tagOriginalRawFileData (tcOriginalRawFileData,
6491 ttUndefined,
6492 negative.OriginalRawFileDataLength (),
6493 negative.OriginalRawFileData ());
6494
6495 tag_uint8_ptr tagOriginalRawFileDigest (tcOriginalRawFileDigest,
6496 negative.OriginalRawFileDigest ().data,
6497 16);
6498
6499 if (negative.OriginalRawFileData ())
6500 {
6501
6502 mainIFD.Add (&tagOriginalRawFileData);
6503
6504 mainIFD.Add (&tagOriginalRawFileDigest);
6505
6506 }
6507
6508 // XMP metadata.
6509
6510 #if qDNGUseXMP
6511
6512 tag_xmp tagXMP (metadata->GetXMP ());
6513
6514 if (tagXMP.Count ())
6515 {
6516
6517 mainIFD.Add (&tagXMP);
6518
6519 }
6520
6521 #endif
6522
6523 // Exif tags.
6524
6525 exif_tag_set exifSet (mainIFD,
6526 *metadata->GetExif (),
6527 metadata->IsMakerNoteSafe (),
6528 metadata->MakerNoteData (),
6529 metadata->MakerNoteLength (),
6530 true);
6531
6532 // Private data.
6533
6534 tag_uint8_ptr tagPrivateData (tcDNGPrivateData,
6535 negative.PrivateData (),
6536 negative.PrivateLength ());
6537
6538 if (negative.PrivateLength ())
6539 {
6540
6541 mainIFD.Add (&tagPrivateData);
6542
6543 }
6544
6545 // Proxy size tags.
6546
6547 uint32 originalDefaultFinalSizeData [2];
6548
6549 originalDefaultFinalSizeData [0] = negative.OriginalDefaultFinalSize ().h;
6550 originalDefaultFinalSizeData [1] = negative.OriginalDefaultFinalSize ().v;
6551
6552 tag_uint32_ptr tagOriginalDefaultFinalSize (tcOriginalDefaultFinalSize,
6553 originalDefaultFinalSizeData,
6554 2);
6555
6556 if (saveOriginalDefaultFinalSize)
6557 {
6558
6559 mainIFD.Add (&tagOriginalDefaultFinalSize);
6560
6561 }
6562
6563 uint32 originalBestQualityFinalSizeData [2];
6564
6565 originalBestQualityFinalSizeData [0] = negative.OriginalBestQualityFinalSize ().h;
6566 originalBestQualityFinalSizeData [1] = negative.OriginalBestQualityFinalSize ().v;
6567
6568 tag_uint32_ptr tagOriginalBestQualityFinalSize (tcOriginalBestQualityFinalSize,
6569 originalBestQualityFinalSizeData,
6570 2);
6571
6572 if (saveOriginalBestQualityFinalSize)
6573 {
6574
6575 mainIFD.Add (&tagOriginalBestQualityFinalSize);
6576
6577 }
6578
6579 dng_urational originalDefaultCropSizeData [2];
6580
6581 originalDefaultCropSizeData [0] = negative.OriginalDefaultCropSizeH ();
6582 originalDefaultCropSizeData [1] = negative.OriginalDefaultCropSizeV ();
6583
6584 tag_urational_ptr tagOriginalDefaultCropSize (tcOriginalDefaultCropSize,
6585 originalDefaultCropSizeData,
6586 2);
6587
6588 if (saveOriginalDefaultCropSize)
6589 {
6590
6591 mainIFD.Add (&tagOriginalDefaultCropSize);
6592
6593 }
6594
6595 // Opcode list 1.
6596
6597 AutoPtr<dng_memory_block> opcodeList1Data (negative.OpcodeList1 ().Spool (host));
6598
6599 tag_data_ptr tagOpcodeList1 (tcOpcodeList1,
6600 ttUndefined,
6601 opcodeList1Data.Get () ? opcodeList1Data->LogicalSize () : 0,
6602 opcodeList1Data.Get () ? opcodeList1Data->Buffer () : NULL);
6603
6604 if (opcodeList1Data.Get ())
6605 {
6606
6607 rawIFD.Add (&tagOpcodeList1);
6608
6609 }
6610
6611 // Opcode list 2.
6612
6613 AutoPtr<dng_memory_block> opcodeList2Data (negative.OpcodeList2 ().Spool (host));
6614
6615 tag_data_ptr tagOpcodeList2 (tcOpcodeList2,
6616 ttUndefined,
6617 opcodeList2Data.Get () ? opcodeList2Data->LogicalSize () : 0,
6618 opcodeList2Data.Get () ? opcodeList2Data->Buffer () : NULL);
6619
6620 if (opcodeList2Data.Get ())
6621 {
6622
6623 rawIFD.Add (&tagOpcodeList2);
6624
6625 }
6626
6627 // Opcode list 3.
6628
6629 AutoPtr<dng_memory_block> opcodeList3Data (negative.OpcodeList3 ().Spool (host));
6630
6631 tag_data_ptr tagOpcodeList3 (tcOpcodeList3,
6632 ttUndefined,
6633 opcodeList3Data.Get () ? opcodeList3Data->LogicalSize () : 0,
6634 opcodeList3Data.Get () ? opcodeList3Data->Buffer () : NULL);
6635
6636 if (opcodeList3Data.Get ())
6637 {
6638
6639 rawIFD.Add (&tagOpcodeList3);
6640
6641 }
6642
6643 // Transparency mask, if any.
6644
6645 AutoPtr<dng_ifd> maskInfo;
6646
6647 AutoPtr<dng_tiff_directory> maskIFD;
6648
6649 AutoPtr<dng_basic_tag_set> maskBasic;
6650
6651 if (hasTransparencyMask)
6652 {
6653
6654 // Create mask IFD.
6655
6656 maskInfo.Reset (new dng_ifd);
6657
6658 maskInfo->fNewSubFileType = sfTransparencyMask;
6659
6660 maskInfo->fImageWidth = negative.RawTransparencyMask ()->Bounds ().W ();
6661 maskInfo->fImageLength = negative.RawTransparencyMask ()->Bounds ().H ();
6662
6663 maskInfo->fSamplesPerPixel = 1;
6664
6665 maskInfo->fBitsPerSample [0] = negative.RawTransparencyMaskBitDepth ();
6666
6667 maskInfo->fPhotometricInterpretation = piTransparencyMask;
6668
6669 maskInfo->fCompression = uncompressed ? ccUncompressed : ccDeflate;
6670 maskInfo->fPredictor = uncompressed ? cpNullPredictor : cpHorizontalDifference;
6671
6672 if (negative.RawTransparencyMask ()->PixelType () == ttFloat)
6673 {
6674
6675 maskInfo->fSampleFormat [0] = sfFloatingPoint;
6676
6677 if (maskInfo->fCompression == ccDeflate)
6678 {
6679 maskInfo->fPredictor = cpFloatingPoint;
6680 }
6681
6682 }
6683
6684 if (maskInfo->fCompression == ccDeflate)
6685 {
6686 maskInfo->FindTileSize (512 * 1024);
6687 }
6688 else
6689 {
6690 maskInfo->SetSingleStrip ();
6691 }
6692
6693 // Create mask tiff directory.
6694
6695 maskIFD.Reset (new dng_tiff_directory);
6696
6697 // Add mask basic tag set.
6698
6699 maskBasic.Reset (new dng_basic_tag_set (*maskIFD, *maskInfo));
6700
6701 }
6702
6703 // Add other subfiles.
6704
6705 uint32 subFileCount = thumbnail ? 1 : 0;
6706
6707 if (hasTransparencyMask)
6708 {
6709 subFileCount++;
6710 }
6711
6712 // Add previews.
6713
6714 uint32 previewCount = previewList ? previewList->Count () : 0;
6715
6716 AutoPtr<dng_tiff_directory> previewIFD [kMaxDNGPreviews];
6717
6718 AutoPtr<dng_basic_tag_set> previewBasic [kMaxDNGPreviews];
6719
6720 for (j = 0; j < previewCount; j++)
6721 {
6722
6723 if (thumbnail != &previewList->Preview (j))
6724 {
6725
6726 previewIFD [j] . Reset (new dng_tiff_directory);
6727
6728 previewBasic [j] . Reset (previewList->Preview (j).AddTagSet (*previewIFD [j]));
6729
6730 subFileCount++;
6731
6732 }
6733
6734 }
6735
6736 // And a link to the raw and JPEG image IFDs.
6737
6738 uint32 subFileData [kMaxDNGPreviews + 2];
6739
6740 tag_uint32_ptr tagSubFile (tcSubIFDs,
6741 subFileData,
6742 subFileCount);
6743
6744 if (subFileCount)
6745 {
6746
6747 mainIFD.Add (&tagSubFile);
6748
6749 }
6750
6751 // Skip past the header and IFDs for now.
6752
6753 uint32 currentOffset = 8;
6754
6755 currentOffset += mainIFD.Size ();
6756
6757 uint32 subFileIndex = 0;
6758
6759 if (thumbnail)
6760 {
6761
6762 subFileData [subFileIndex++] = currentOffset;
6763
6764 currentOffset += rawIFD.Size ();
6765
6766 }
6767
6768 if (hasTransparencyMask)
6769 {
6770
6771 subFileData [subFileIndex++] = currentOffset;
6772
6773 currentOffset += maskIFD->Size ();
6774
6775 }
6776
6777 for (j = 0; j < previewCount; j++)
6778 {
6779
6780 if (thumbnail != &previewList->Preview (j))
6781 {
6782
6783 subFileData [subFileIndex++] = currentOffset;
6784
6785 currentOffset += previewIFD [j]->Size ();
6786
6787 }
6788
6789 }
6790
6791 exifSet.Locate (currentOffset);
6792
6793 currentOffset += exifSet.Size ();
6794
6795 stream.SetWritePosition (currentOffset);
6796
6797 // Write the extra profiles.
6798
6799 if (extraProfileCount)
6800 {
6801
6802 for (j = 0; j < extraProfileCount; j++)
6803 {
6804
6805 extraProfileOffsets.Buffer_uint32 () [j] = (uint32) stream.Position ();
6806
6807 uint32 index = extraProfileIndex [j];
6808
6809 const dng_camera_profile &profile (negative.ProfileByIndex (index));
6810
6811 tiff_dng_extended_color_profile extraWriter (profile);
6812
6813 extraWriter.Put (stream, false);
6814
6815 }
6816
6817 }
6818
6819 // Write the thumbnail data.
6820
6821 if (thumbnail)
6822 {
6823
6824 thumbnail->WriteData (host,
6825 *this,
6826 *thmBasic,
6827 stream);
6828
6829 }
6830
6831 // Write the preview data.
6832
6833 for (j = 0; j < previewCount; j++)
6834 {
6835
6836 if (thumbnail != &previewList->Preview (j))
6837 {
6838
6839 previewList->Preview (j).WriteData (host,
6840 *this,
6841 *previewBasic [j],
6842 stream);
6843
6844 }
6845
6846 }
6847
6848 // Write the raw data.
6849
6850 if (rawJPEGImage)
6851 {
6852
6853 uint32 tileCount = info.TilesAcross () *
6854 info.TilesDown ();
6855
6856 for (uint32 tileIndex = 0; tileIndex < tileCount; tileIndex++)
6857 {
6858
6859 // Remember this offset.
6860
6861 uint32 tileOffset = (uint32) stream.Position ();
6862
6863 rawBasic.SetTileOffset (tileIndex, tileOffset);
6864
6865 // Write JPEG data.
6866
6867 stream.Put (rawJPEGImage->fJPEGData [tileIndex]->Buffer (),
6868 rawJPEGImage->fJPEGData [tileIndex]->LogicalSize ());
6869
6870 // Update tile count.
6871
6872 uint32 tileByteCount = (uint32) stream.Position () - tileOffset;
6873
6874 rawBasic.SetTileByteCount (tileIndex, tileByteCount);
6875
6876 // Keep the tiles on even byte offsets.
6877
6878 if (tileByteCount & 1)
6879 {
6880 stream.Put_uint8 (0);
6881 }
6882
6883 }
6884
6885 }
6886
6887 else
6888 {
6889
6890 #if qDNGValidate
6891 dng_timer timer ("Write raw image time");
6892 #endif
6893
6894 WriteImage (host,
6895 info,
6896 rawBasic,
6897 stream,
6898 rawImage,
6899 fakeChannels);
6900
6901 }
6902
6903 // Write transparency mask image.
6904
6905 if (hasTransparencyMask)
6906 {
6907
6908 #if qDNGValidate
6909 dng_timer timer ("Write transparency mask time");
6910 #endif
6911
6912 WriteImage (host,
6913 *maskInfo,
6914 *maskBasic,
6915 stream,
6916 *negative.RawTransparencyMask ());
6917
6918 }
6919
6920 // Trim the file to this length.
6921
6922 stream.SetLength (stream.Position ());
6923
6924 // DNG has a 4G size limit.
6925
6926 if (stream.Length () > 0x0FFFFFFFFL)
6927 {
6928 ThrowImageTooBigDNG ();
6929 }
6930
6931 // Write TIFF Header.
6932
6933 stream.SetWritePosition (0);
6934
6935 stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII);
6936
6937 stream.Put_uint16 (42);
6938
6939 stream.Put_uint32 (8);
6940
6941 // Write the IFDs.
6942
6943 mainIFD.Put (stream);
6944
6945 if (thumbnail)
6946 {
6947
6948 rawIFD.Put (stream);
6949
6950 }
6951
6952 if (hasTransparencyMask)
6953 {
6954
6955 maskIFD->Put (stream);
6956
6957 }
6958
6959 for (j = 0; j < previewCount; j++)
6960 {
6961
6962 if (thumbnail != &previewList->Preview (j))
6963 {
6964
6965 previewIFD [j]->Put (stream);
6966
6967 }
6968
6969 }
6970
6971 exifSet.Put (stream);
6972
6973 stream.Flush ();
6974
6975 }
6976
6977/*****************************************************************************/
6978