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 
dng_resolution()65 dng_resolution::dng_resolution ()
66 
67 	:	fXResolution ()
68 	,	fYResolution ()
69 
70 	,	fResolutionUnit (0)
71 
72 	{
73 
74 	}
75 
76 /******************************************************************************/
77 
SpoolAdobeData(dng_stream & stream,const dng_metadata * metadata,const dng_jpeg_preview * preview,const dng_memory_block * imageResources)78 static 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 
BuildAdobeData(dng_host & host,const dng_metadata * metadata,const dng_jpeg_preview * preview,const dng_memory_block * imageResources)186 static 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 
tag_string(uint16 code,const dng_string & s,bool forceASCII)205 tag_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 
Put(dng_stream & stream) const241 void tag_string::Put (dng_stream &stream) const
242 	{
243 
244 	stream.Put (fString.Get (), Size ());
245 
246 	}
247 
248 /*****************************************************************************/
249 
tag_encoded_text(uint16 code,const dng_string & text)250 tag_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 
Put(dng_stream & stream) const279 void 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 
Put(dng_stream & stream) const313 void 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 
tag_matrix(uint16 code,const dng_matrix & m)415 tag_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 
tag_icc_profile(const void * profileData,uint32 profileSize)438 tag_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 
Put(dng_stream & stream) const460 void 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 
tag_exif_date_time(uint16 code,const dng_date_time & dt)478 tag_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 
tag_iptc(const void * data,uint32 length)503 tag_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 
Put(dng_stream & stream) const517 void 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 
tag_xmp(const dng_xmp * xmp)540 tag_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 
Add(const tiff_tag * tag)572 void 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 
Size() const610 uint32 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 
Put(dng_stream & stream,OffsetsBase offsetsBase,uint32 explicitBase) const637 void 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 
dng_basic_tag_set(dng_tiff_directory & directory,const dng_ifd & info)716 dng_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 
exif_tag_set(dng_tiff_directory & directory,const dng_exif & exif,bool makerNoteSafe,const void * makerNoteData,uint32 makerNoteLength,bool insideDNG)876 exif_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 
AddLinks(dng_tiff_directory & directory)1658 void 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 
1683 class 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 
range_tag_set(dng_tiff_directory & directory,const dng_negative & negative)1729 range_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 
1958 class 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 
mosaic_tag_set(dng_tiff_directory & directory,const dng_mosaic_info & info)1989 mosaic_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 
2074 class 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 
color_tag_set(dng_tiff_directory & directory,const dng_negative & negative)2111 color_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 
2225 class 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 
profile_tag_set(dng_tiff_directory & directory,const dng_camera_profile & profile)2284 profile_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 
tiff_dng_extended_color_profile(const dng_camera_profile & profile)2591 tiff_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 
Put(dng_stream & stream,bool includeModelRestriction)2602 void 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 
tag_dng_noise_profile(const dng_noise_profile & profile)2643 tag_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 
dng_image_writer()2667 dng_image_writer::dng_image_writer ()
2668 	{
2669 
2670 	}
2671 
2672 /*****************************************************************************/
2673 
~dng_image_writer()2674 dng_image_writer::~dng_image_writer ()
2675 	{
2676 
2677 	}
2678 
2679 /*****************************************************************************/
2680 
CompressedBufferSize(const dng_ifd & ifd,uint32 uncompressedSize)2681 uint32 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 
EncodeDelta8(uint8 * dPtr,uint32 rows,uint32 cols,uint32 channels)2735 static 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 
EncodeDelta16(uint16 * dPtr,uint32 rows,uint32 cols,uint32 channels)2766 static 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 
EncodeDelta32(uint32 * dPtr,uint32 rows,uint32 cols,uint32 channels)2797 static 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 
EncodeDeltaBytes(uint8 * bytePtr,int32 cols,int32 channels)2828 inline 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 
EncodeFPDelta(uint8 * buffer,uint8 * temp,int32 cols,int32 channels,int32 bytesPerSample)2911 static 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 
EncodePredictor(dng_host & host,const dng_ifd & ifd,dng_pixel_buffer & buffer,AutoPtr<dng_memory_block> & tempBuffer)3006 void 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 
ByteSwapBuffer(dng_host &,dng_pixel_buffer & buffer)3143 void 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 
ReorderSubTileBlocks(const dng_ifd & ifd,dng_pixel_buffer & buffer,AutoPtr<dng_memory_block> & uncompressedBuffer,AutoPtr<dng_memory_block> & subTileBlockBuffer)3181 void 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 
3248 class 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 
SearchTable(int32 w,int32 k) const3299 		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 
dng_lzw_compressor()3336 dng_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 
InitTable()3356 void 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 
AddTable(int32 w,int32 k)3381 void 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 
PutCodeWord(int32 code)3426 void 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 
Compress(const uint8 * sPtr,uint8 * dPtr,uint32 sCount,uint32 & dCount)3483 void 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 
dng_error_exit(j_common_ptr cinfo)3562 static 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 
dng_output_message(j_common_ptr cinfo)3591 static 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 
3608 struct 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 
dng_init_destination(j_compress_ptr cinfo)3621 static 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 
dng_empty_output_buffer(j_compress_ptr cinfo)3633 static 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 
dng_term_destination(j_compress_ptr cinfo)3649 static 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 
jpeg_set_adobe_quality(struct jpeg_compress_struct * cinfo,int32 quality)3666 static 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 
WriteData(dng_host & host,const dng_ifd & ifd,dng_stream & stream,dng_pixel_buffer & buffer,AutoPtr<dng_memory_block> & compressedBuffer)3711 void 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 
EncodeJPEGPreview(dng_host & host,const dng_image & image,dng_jpeg_preview & preview,int32 quality)4013 void 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 
WriteTile(dng_host & host,const dng_ifd & ifd,dng_stream & stream,const dng_image & image,const dng_rect & tileArea,uint32 fakeChannels,AutoPtr<dng_memory_block> & compressedBuffer,AutoPtr<dng_memory_block> & uncompressedBuffer,AutoPtr<dng_memory_block> & subTileBlockBuffer,AutoPtr<dng_memory_block> & tempBuffer)4167 void 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 
4305 class 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 
dng_write_tiles_task(dng_image_writer & imageWriter,dng_host & host,const dng_ifd & ifd,dng_basic_tag_set & basic,dng_stream & stream,const dng_image & image,uint32 fakeChannels,uint32 tilesDown,uint32 tilesAcross,uint32 compressedSize,uint32 uncompressedSize)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 
Process(uint32,const dng_rect &,dng_abort_sniffer * sniffer)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 
WriteImage(dng_host & host,const dng_ifd & ifd,dng_basic_tag_set & basic,dng_stream & stream,const dng_image & image,uint32 fakeChannels)4568 void 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 
CopyString(const dng_xmp & oldXMP,dng_xmp & newXMP,const char * ns,const char * path,dng_string * exif=NULL)4774 static 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 
CopyStringList(const dng_xmp & oldXMP,dng_xmp & newXMP,const char * ns,const char * path,bool isBag)4806 static 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 
CopyAltLangDefault(const dng_xmp & oldXMP,dng_xmp & newXMP,const char * ns,const char * path,dng_string * exif=NULL)4831 static 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 
CopyStructField(const dng_xmp & oldXMP,dng_xmp & newXMP,const char * ns,const char * path,const char * field)4863 static 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 
CopyBoolean(const dng_xmp & oldXMP,dng_xmp & newXMP,const char * ns,const char * path)4888 static 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 
CleanUpMetadata(dng_host & host,dng_metadata & metadata,dng_metadata_subset metadataSubset,const char * dstMIMI,const char * software)4909 void 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 
WriteTIFF(dng_host & host,dng_stream & stream,const dng_image & image,uint32 photometricInterpretation,uint32 compression,dng_negative * negative,const dng_color_space * space,const dng_resolution * resolution,const dng_jpeg_preview * thumbnail,const dng_memory_block * imageResources,dng_metadata_subset metadataSubset)5256 void 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 
WriteTIFF(dng_host & host,dng_stream & stream,const dng_image & image,uint32 photometricInterpretation,uint32 compression,const dng_metadata * metadata,const dng_color_space * space,const dng_resolution * resolution,const dng_jpeg_preview * thumbnail,const dng_memory_block * imageResources,dng_metadata_subset metadataSubset)5285 void 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 
WriteTIFFWithProfile(dng_host & host,dng_stream & stream,const dng_image & image,uint32 photometricInterpretation,uint32 compression,dng_negative * negative,const void * profileData,uint32 profileSize,const dng_resolution * resolution,const dng_jpeg_preview * thumbnail,const dng_memory_block * imageResources,dng_metadata_subset metadataSubset)5329 void 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 
WriteTIFFWithProfile(dng_host & host,dng_stream & stream,const dng_image & image,uint32 photometricInterpretation,uint32 compression,const dng_metadata * constMetadata,const void * profileData,uint32 profileSize,const dng_resolution * resolution,const dng_jpeg_preview * thumbnail,const dng_memory_block * imageResources,dng_metadata_subset metadataSubset)5360 void 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 
WriteDNG(dng_host & host,dng_stream & stream,dng_negative & negative,const dng_preview_list * previewList,uint32 maxBackwardVersion,bool uncompressed)5596 void 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 
WriteDNG(dng_host & host,dng_stream & stream,const dng_negative & negative,const dng_metadata & constMetadata,const dng_preview_list * previewList,uint32 maxBackwardVersion,bool uncompressed)5616 void 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