cvsimport
[beagle.git] / Util / Tiff.cs
blob595e02d682fe693d5fe9cfb5d1399dc0e0ebfe71
1 using Beagle.Util;
2 using SemWeb;
3 using System;
5 namespace Beagle.Util.Tiff {
7 // This is primarily to preserve the names from the specification
8 // because they differ from the tiff standard names
9 public enum NiffId : ushort {
10 SubfileType = 0x00fe,
11 PelPathLength = 0x0100,
12 LineProgressionLength = 257,
13 BitsPerSample = 0x0101,
14 PhotometricInterpretation = 0x0106,
15 DataOffset = 0x0111,
16 SamplesPerPixel = 0x0115,
17 DataByteCounts = 0x0117,
18 PelPathResolution = 0x011a,
19 LineProgressionResolution = 0x011b,
20 ResolutionUnit = 0x0128,
21 ColumnsPerPelPath = 322,
22 RowsPerLineProgression = 323,
23 Rotation = 33465,
24 NavyCompression = 33466,
25 TileIndex = 33467
28 public enum TagId : ushort {
29 InteroperabilityIndex = 0x0001,
30 InteroperabilityVersion = 0x0002,
32 NewSubfileType = 0x00fe,
33 SubfileType = 0x00ff,
35 ImageWidth = 0x0100,
36 ImageLength = 0x0101,
37 BitsPerSample = 0x0102,
38 Compression = 0x0103,
39 PhotometricInterpretation = 0x0106,
40 FillOrder = 0x010a,
41 DocumentName = 0x010d,
42 ImageDescription = 0x010e,
43 Make = 0x010f,
44 Model = 0x0110,
45 StripOffsets = 0x0111,
46 Orientation = 0x0112,
47 SamplesPerPixel = 0x0115,
48 RowsPerStrip = 0x0116,
49 StripByteCounts = 0x0117,
50 XResolution = 0x011a,
51 YResolution = 0x011b,
52 PlanarConfiguration = 0x011c,
54 T4Options = 0x0124,
55 T6Options = 0x0125,
57 ResolutionUnit = 0x0128,
58 TransferFunction = 0x012d,
59 Software = 0x0131,
60 DateTime = 0x0132,
61 Artist = 0x013b,
62 WhitePoint = 0x013e,
63 PrimaryChromaticities = 0x013f,
65 HalftoneHints = 0x0141,
66 // Tiled images
67 TileWidth = 0x0142,
68 TileLength = 0x0143,
69 TileOffsets = 0x0144,
70 TileByteCounts = 0x0145,
72 SubIFDs = 0x014a, // TIFF-EP
74 // CMYK images
75 InkSet = 0x014c,
76 NumberOfInks = 0x014e,
77 InkNames = 0x014d,
78 DotRange = 0x0150,
79 TargetPrinter = 0x0151,
80 ExtraSamples = 0x0152,
81 SampleFormat = 0x0153,
82 SMinSampleValue = 0x0154,
83 SMaxSampleValue = 0x0155,
85 TransferRange = 0x0156,
87 ClipPath = 0x0157, // TIFF PageMaker Technote #2.
89 JPEGTables = 0x015b, // TIFF-EP
91 JPEGProc = 0x0200,
92 JPEGInterchangeFormat = 0x0201,
93 JPEGInterchangeFormatLength = 0x0202,
94 JPEGRestartInterval = 0x0203,
95 JPEGLosslessPredictors = 0x0205,
96 JPEGPointTransforms = 0x0206,
97 JPEGQTables = 0x0207,
98 JPEGDCTables = 0x0208,
99 JPEGACTables = 0x0209,
101 YCbCrCoefficients = 0x0211,
102 YCbCrSubSampling = 0x0212,
103 YCbCrPositioning = 0x0213,
105 ReferenceBlackWhite = 0x0214,
106 RelatedImageFileFormat = 0x1000,
107 RelatedImageWidth = 0x1001,
108 RelatedImageLength = 0x1002,
109 CFARepeatPatternDim = 0x828d,
110 CFAPattern = 0x828e,
111 BatteryLevel = 0x828f,
112 Copyright = 0x8298,
113 ExposureTime = 0x829a,
114 FNumber = 0x829d,
116 // These are from the NIFF spec and only really valid when the header begins with IIN1
117 // see the NiffTag enum for the specifcation specific names
118 Rotation = 0x82b9,
119 NavyCompression = 0x82ba,
120 TileIndex = 0x82bb,
121 // end NIFF specific
123 IPTCNAA = 0x83bb,
125 PhotoshopPrivate = 0x8649,
127 ExifIfdPointer = 0x8769,
128 InterColorProfile = 0x8773,
129 ExposureProgram = 0x8822,
130 SpectralSensitivity = 0x8824,
131 GPSInfoIfdPointer = 0x8825,
132 ISOSpeedRatings = 0x8827,
133 OECF = 0x8828,
134 ExifVersion = 0x9000,
135 DateTimeOriginal = 0x9003,
136 DateTimeDigitized = 0x9004,
137 ComponentsConfiguration = 0x9101,
138 CompressedBitsPerPixel = 0x9102,
139 ShutterSpeedValue = 0x9201,
140 ApertureValue = 0x9202,
141 BrightnessValue = 0x9203,
142 ExposureBiasValue = 0x9204,
143 MaxApertureValue = 0x9205,
144 SubjectDistance = 0x9206,
145 MeteringMode = 0x9207,
146 LightSource = 0x9208,
147 Flash = 0x9209,
148 FocalLength = 0x920a,
150 FlashEnergy_TIFFEP = 0x920b,// TIFF-EP
151 SpacialFrequencyResponse = 0x920c,// TIFF-EP
152 Noise = 0x920d,// TIFF-EP
153 FocalPlaneXResolution_TIFFEP = 0x920e,// TIFF-EP
154 FocalPlaneYResolution_TIFFEP = 0x920f,// TIFF-EP
155 FocalPlaneResolutionUnit_TIFFEP = 0x9210,// TIFF-EP
156 ImageName = 0x9211,// TIFF-EP
157 SecurityClassification = 0x9212,// TIFF-EP
159 ImageHistory = 0x9213, // TIFF-EP null separated list
161 SubjectArea = 0x9214,
163 ExposureIndex_TIFFEP = 0x9215, // TIFF-EP
164 TIFFEPStandardID = 0x9216, // TIFF-EP
165 SensingMethod_TIFFEP = 0x9217, // TIFF-EP
167 MakerNote = 0x927c,
168 UserComment = 0x9286,
169 SubSecTime = 0x9290,
170 SubSecTimeOriginal = 0x9291,
171 SubSecTimeDigitized = 0x9292,
172 FlashPixVersion = 0xa000,
173 ColorSpace = 0xa001,
174 PixelXDimension = 0xa002,
175 PixelYDimension = 0xa003,
176 RelatedSoundFile = 0xa004,
177 InteroperabilityIfdPointer = 0xa005,
178 FlashEnergy = 0xa20b,
179 SpatialFrequencyResponse = 0xa20c,
180 FocalPlaneXResolution = 0xa20e,
181 FocalPlaneYResolution = 0xa20f,
182 FocalPlaneResolutionUnit = 0xa210,
183 SubjectLocation = 0xa214,
184 ExposureIndex = 0xa215,
185 SensingMethod = 0xa217,
186 FileSource = 0xa300,
187 SceneType = 0xa301,
188 ExifCFAPattern = 0xa302,
189 CustomRendered = 0xa401,
190 ExposureMode = 0xa402,
191 WhiteBalance = 0xa403,
192 DigitalZoomRatio = 0xa404,
193 FocalLengthIn35mmFilm = 0xa405,
194 SceneCaptureType = 0xa406,
195 GainControl = 0xa407,
196 Contrast = 0xa408,
197 Saturation = 0xa409,
198 Sharpness = 0xa40a,
199 DeviceSettingDescription = 0xa40b,
200 SubjectDistanceRange = 0xa40c,
201 ImageUniqueId = 0xa420,
203 // The Following IDs are not described the EXIF spec
204 Gamma = 0xa500,
206 // The XMP spec declares that XMP data should live 0x2bc when
207 // embedded in tiff images.
208 XMP = 0x02bc,
210 // from the dng spec
211 DNGVersion = 0xc612, // Ifd0
212 DNGBackwardVersion = 0xc613, // Ifd0
213 UniqueCameraModel = 0xc614, // Ifd0
214 LocalizedCameraModel = 0xc615, // Ifd0
215 CFAPlaneColor = 0xc616, // RawIfd
216 CFALayout = 0xc617, // RawIfd
217 LinearizationTable = 0xc618, // RawIfd
218 BlackLevelRepeatDim = 0xc619, // RawIfd
219 BlackLevel = 0xc61a, // RawIfd
220 BlackLevelDeltaH = 0xc61b, // RawIfd
221 BlackLevelDeltaV = 0xc61c, // RawIfd
222 WhiteLevel = 0xc61d, // RawIfd
223 DefaultScale = 0xc61e, // RawIfd
224 DefaultCropOrigin = 0xc61f, // RawIfd
225 DefaultCropSize = 0xc620, // RawIfd
226 ColorMatrix1 = 0xc621, // Ifd0
227 ColorMatrix2 = 0xc622, // Ifd0
228 CameraCalibration1 = 0xc623, // Ifd0
229 CameraCalibration2 = 0xc624, // Ifd0
230 ReductionMatrix1 = 0xc625, // Ifd0
231 ReductionMatrix2 = 0xc626, // Ifd0
232 AnalogBalance = 0xc627, // Ifd0
233 AsShotNetural = 0xc628, // Ifd0
234 AsShotWhiteXY = 0xc629, // Ifd0
235 BaselineExposure = 0xc62a, // Ifd0
236 BaselineNoise = 0xc62b, // Ifd0
237 BaselineSharpness = 0xc62c, // Ifd0
238 BayerGreeSpit = 0xc62d, // Ifd0
239 LinearResponseLimit = 0xc62e, // Ifd0
240 CameraSerialNumber = 0xc62f, // Ifd0
241 LensInfo = 0xc630, // Ifd0
242 ChromaBlurRadius = 0xc631, // RawIfd
243 AntiAliasStrength = 0xc632, // RawIfd
244 DNGPrivateData = 0xc634, // Ifd0
246 MakerNoteSafety = 0xc635, // Ifd0
248 // The Spec says BestQualityScale is 0xc635 but it appears to be wrong
249 //BestQualityScale = 0xc635, // RawIfd
250 BestQualityScale = 0xc63c, // RawIfd this looks like the correct value
252 CalibrationIlluminant1 = 0xc65a, // Ifd0
253 CalibrationIlluminant2 = 0xc65b, // Ifd0
255 // Print Image Matching data
256 PimIfdPointer = 0xc4a5
259 public struct SRational {
260 public int Numerator;
261 public int Denominator;
263 public SRational (byte [] raw_data, int offset, bool little)
265 Numerator = EndianConverter.ToInt32 (raw_data, offset, little);
266 Denominator = EndianConverter.ToInt32 (raw_data, offset, little);
269 public SRational (int numerator, int denominator)
271 Numerator = numerator;
272 Denominator = denominator;
275 public static SRational BitwiseCopy (Rational rational)
277 SRational result;
279 result.Numerator = unchecked ((int) rational.Numerator);
280 result.Denominator = unchecked ((int) rational.Denominator);
281 return result;
284 public override string ToString ()
286 if (Numerator == 0)
287 return "0";
288 else if (Numerator % Denominator == 0)
289 return String.Format ("{0}", Numerator / Denominator);
290 else if (Denominator % Numerator == 0)
291 return String.Format ("1/{0}", Denominator / Numerator);
293 return String.Format ("{0}/{1}", Numerator, Denominator);
296 public double Value {
297 get {
298 return Numerator / (double)Denominator;
303 public struct Rational {
304 public uint Numerator;
305 public uint Denominator;
307 public Rational (uint numerator, uint denominator)
309 Numerator = numerator;
310 Denominator = denominator;
313 public Rational (string value)
315 string [] vals = value.Split ('/');
316 if (vals.Length == 2) {
317 this.Numerator = UInt32.Parse (vals [0]);
318 this.Denominator = UInt32.Parse (vals [1]);
319 return;
320 } if (vals.Length == 1) {
321 double tmp = Double.Parse (value);
322 this.Numerator = (uint) (tmp * 100000);
323 this.Denominator = 100000;
324 } else
325 throw new System.Exception ("unable to parse rational value");
328 public override string ToString ()
330 if (Numerator == 0)
331 return "0";
332 else if (Numerator % Denominator == 0)
333 return String.Format ("{0}", Numerator / Denominator);
334 else if (Denominator % Numerator == 0)
335 return String.Format ("1/{0}", Denominator / Numerator);
337 return String.Format ("{0}/{1}", Numerator, Denominator);
340 public double Value {
341 get {
342 return Numerator / (double)Denominator;
347 struct UserComment {
348 string Charset;
349 public string Value;
351 public UserComment (byte [] raw_data, bool little)
353 string charset = System.Text.Encoding.ASCII.GetString (raw_data, 0, 8);
354 System.Text.Encoding enc;
356 switch (charset) {
357 case "ASCII\0\0\0":
358 enc = System.Text.Encoding.ASCII;
359 break;
360 case "UNICODE\0":
361 enc = System.Text.Encoding.BigEndianUnicode;
362 break;
363 case "SJIS\0\0\0\0":
364 // FIXME I'm pretty sure this isn't actually the encoding name.
365 enc = System.Text.Encoding.GetEncoding ("SJIS");
366 break;
367 case "\0\0\0\0\0\0\0\0":
368 // FIXME the spec says to use the local encoding in this case, we could probably
369 // do something smarter, but whatever.
370 enc = System.Text.Encoding.Default;
371 break;
372 default:
373 enc = null;
374 throw new System.Exception (System.String.Format ("Invalid charset name: {0}", charset));
377 Charset = charset;
378 Value = enc.GetString (raw_data, 8, raw_data.Length - 8);
382 public struct CFAPattern {
383 public ushort Rows;
384 public ushort Columns;
385 public byte [] Values;
387 public CFAPattern (byte [] raw_data, bool little)
389 Columns = EndianConverter.ToUInt16 (raw_data, 0, little);
390 Rows = EndianConverter.ToUInt16 (raw_data, 2, little);
392 Values = new byte [Rows * Columns];
393 System.Array.Copy (raw_data, 4, Values, 0, Values.Length);
397 * Note the Exif spec defines a CFA pattern tag that includes the row and column counts as shorts
398 * inside the first four bytes of the entry. The Tiff-EP standard define the CFARepeatPattern tag
399 * that contains the row and column counts presumably since the Exif version wouldn't allow you to
400 * alter the endian of the file without knowing the tag layout.
403 public CFAPattern (ushort rows, ushort cols, byte [] raw_data, bool little)
405 Columns = rows;
406 Rows = cols;
407 Values = new byte [rows * cols];
408 System.Array.Copy (raw_data, 0, Values, 0, Values.Length);
412 public struct OECFTable {
413 public ushort Rows;
414 public ushort Columns;
415 public string [] Names;
416 public SRational [] Values;
418 public OECFTable (byte [] raw_data, bool little)
420 Columns = EndianConverter.ToUInt16 (raw_data, 0, little);
421 Rows = EndianConverter.ToUInt16 (raw_data, 2, little);
422 Names = new string [Columns];
423 Values = new SRational [Columns * Rows];
425 int pos = 2;
426 int i;
427 int type_size = DirectoryEntry.GetTypeSize (EntryType.SRational);
429 for (i = 0; i < Names.Length; i++)
430 Names [i] = ReadString (raw_data, ref pos);
432 for (i = 0; i < Values.Length; i++)
433 Values [i] = new SRational (raw_data, pos + i * type_size, little);
437 public string ReadString (byte [] data, ref int pos)
439 int start = pos;
440 for (; pos < data.Length; pos++) {
441 if (data [pos] == 0)
442 break;
444 return System.Text.Encoding.ASCII.GetString (data, start, pos - start);
448 public enum ExtraSamples {
449 Unspecified = 0,
450 AssociatedAlpha = 1,
451 UnassociatedAlpa = 2
454 public enum FileSource {
455 DCF = 3,
458 public enum PhotometricInterpretation : ushort {
459 WhiteIsZero = 0,
460 BlackIsZero = 1,
461 RGB = 2,
462 PaletteColor = 3,
463 TransparencyMask = 4,
464 Separated = 5, // CMYK
465 YCbCr = 6,
466 CIELab = 8,
467 ICCLab = 9,
468 ITULab = 10,
469 LogL = 32844, // Log Luminance
470 LogLUV = 32845,
471 ColorFilterArray = 32803, // ColorFilterArray... the good stuff
472 LinearRaw = 34892 // DBG LinearRaw
475 public enum PlanarConfiguration {
476 Chunky = 1,
477 Planar = 2
480 public enum Compression {
481 Packed = 1,
482 Huffman = 2,
483 T4 = 3,
484 T6 = 4,
485 LZW = 5,
486 JPEG = 6,
487 JPEGStream = 7, // TIFF-EP stores full jpeg stream
488 Deflate = 8,
489 JBIG = 9,
490 JBIG_MRC,
491 PackBits = 32773,
492 NikonCompression = 34713,
493 Deflate_experimental = 0x80b2
496 public enum JPEGProc {
497 BaselineSequencial = 1,
498 LosslessHuffman = 14,
501 public enum SubfileType {
502 FullResolution = 1,
503 ReducedResolution = 2,
504 PageOfMultipage = 3
507 public enum ExposureProgram {
508 NotDefined = 0,
509 Manual = 1,
510 NormalProgram = 2, // Normal Program
511 AperturePriority = 3, // Aperture priority
512 ShutterPriorty = 4, // Shutter priority
513 CreativeProgram = 5, // Creative program
514 ActionProgram = 6, // Action program
515 PortraitMode = 7, // Portrait mode
516 LandscapeMode = 8 // Landscape mode
519 public enum ExposureMode {
520 Auto = 0,
521 Manual = 1,
522 AutoBracket = 2
525 public enum CustomRendered : ushort {
526 Normal = 0,
527 Custom = 1
531 public enum SceneType {
532 DirectlyPhotographed = 1
535 public enum MeteringMode {
536 Uknown = 0,
537 Average = 1,
538 CenterWeightedAverage = 2,
539 Spot = 3,
540 MulitSpot = 4,
541 Pattern = 5,
542 Partial = 6,
545 public enum SceneCaptureType : ushort {
546 Standard = 0,
547 Landscape = 1,
548 Portrait = 2,
549 NightScene = 3
552 public enum GainControl : ushort {
553 None = 0,
554 LowGainUp = 1,
555 HighGainUp = 2,
556 LowGainDown = 3,
557 HighGainDown = 4
560 public enum Contrast : ushort {
561 Normal = 0,
562 Soft = 1,
563 Hard = 2
566 public enum Saturation : ushort {
567 Normal = 0,
568 Low = 1,
569 High = 2
572 public enum WhiteBalance : ushort {
573 Auto = 0,
574 Manual = 1
577 public enum Sharpness : ushort {
578 Normal = 0,
579 Soft = 1,
580 Hard = 2
583 public enum LightSource {
584 Unknown = 0,
585 Daylight = 1,
586 Fluorescent = 2,
587 Tungsten = 3,
588 Fash = 4,
589 FineWeather = 9,
590 CloudyWeather = 10,
591 Shade = 11,
592 DaylightFluorescent = 12,
593 DaylightWhiteFluorescent = 13,
594 CoolWhiteFluorescent = 14,
595 WhiteFluorescent = 15,
596 StandardLightA = 17,
597 StandardLightB = 18,
598 StandardLightC = 19,
599 D55 = 20,
600 D65 = 21,
601 D75 = 22,
602 D50 = 23,
603 ISOStudioTungsten = 24,
604 OtherSource = 255
607 public enum ColorSpace : ushort {
608 StandardRGB = 1, // sRGB
609 AdobeRGB = 2,
610 Uncalibrated = 0xffff
613 public enum ComponentsConfiguration {
614 DoesNotExist = 0,
615 Y = 1,
616 Cb = 2,
617 Cr = 3,
618 R = 4,
619 G = 6,
622 public enum ResolutionUnit : ushort {
623 Uncalibrated = 1,
624 Inch = 2,
625 Centimeter = 3
628 public enum SensingMethod : short {
629 NotDefined = 1,
630 OneChipColorAreaSensor = 2,
631 TwoChipColorAreaSensor = 3,
632 ThreeChipColorAreaSensor = 4,
633 ColorSequentialAreaSensor = 5,
634 TrilinearSensor = 7,
635 ColorSequentialLinearSensor = 8
638 [System.Flags]
639 public enum NewSubfileType : uint {
640 ReducedResolution = 1,
641 PageOfMultipage= 1 << 1,
642 TransparencyMask = 1 << 2
645 public enum EntryType {
646 Byte = 1,
647 Ascii,
648 Short,
649 Long,
650 Rational,
651 SByte,
652 Undefined,
653 SShort,
654 SLong,
655 SRational,
656 Float,
657 Double,
658 Ifd // TIFF-EP - TIFF PageMaker TechnicalNote 2
661 public class Tag {
662 public ushort Id;
663 public EntryType Type;
664 public int Count;
665 public string Name;
666 public string Description;
669 public class CanonTag : Tag {
670 // http://www.gvsoft.homedns.org/exif/makernote-canon.html
672 public enum CanonId {
673 Unknown1 = 0x0000,
674 CameraSettings1 = 0x0001,
675 Unknown2 = 0x0003,
676 CameraSettings2 = 0x0004,
677 ImageType = 0x0006,
678 FirmwareVersion = 0x0007,
679 ImageNumber = 0x0008,
680 OwnerName = 0x0009,
681 Unknown3 = 0x000a,
682 CameraSerialNumber = 0x000c,
683 Unknown4 = 0x000d,
684 CustomFunctions = 0x000f
687 public CanonTag (CanonId id, EntryType type, int count, string name, string description)
689 this.Id = (ushort)id;
690 this.Type = type;
691 this.Count = count;
692 this.Name = name;
693 this.Description = description;
696 public static System.Collections.Hashtable Tags;
698 static CanonTag () {
699 CanonTag [] tags = {
700 new CanonTag (CanonId.Unknown1, EntryType.Short, 6, null, null),
701 new CanonTag (CanonId.CameraSettings1, EntryType.Short, -1, "Camera Settings 1", "First Canon MakerNote settings section"),
702 new CanonTag (CanonId.Unknown2, EntryType.Short, 4, null, null),
703 new CanonTag (CanonId.CameraSettings2, EntryType.Short, -1, "Camera Settings 2", "Second Canon MakerNote settings section"),
704 new CanonTag (CanonId.ImageType, EntryType.Ascii, 32, "Image Type", null), // FIXME description
705 new CanonTag (CanonId.FirmwareVersion, EntryType.Ascii, 24, "Firmware Version", "Version of the firmware installed on the camera"),
706 new CanonTag (CanonId.ImageNumber, EntryType.Long, 1, "Image Number", null), // FIXME description
707 new CanonTag (CanonId.OwnerName, EntryType.Long, 32, "Owner Name", "Name of the Camera Owner"), // FIXME description
708 new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
709 new CanonTag (CanonId.CameraSerialNumber, EntryType.Short, 1, "Serial Number", null), //FIXME description
710 new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
711 new CanonTag (CanonId.CustomFunctions, EntryType.Short, -1, "Custom Functions", "Camera Custom Functions")
714 foreach (CanonTag tag in tags)
715 Tags [tag.Id] = tag;
720 public enum Endian {
721 Big,
722 Little
725 public class Converter {
726 public static uint ReadUInt (System.IO.Stream stream, Endian endian)
728 byte [] tmp = new byte [4];
730 if (stream.Read (tmp, 0, tmp.Length) < 4)
731 throw new System.Exception ("Short Read");
733 return EndianConverter.ToUInt32 (tmp, 0, endian == Endian.Little);
736 public static ushort ReadUShort (System.IO.Stream stream, Endian endian)
738 byte [] tmp = new byte [2];
740 if (stream.Read (tmp, 0, tmp.Length) < 2)
741 throw new System.Exception ("Short Read");
743 return EndianConverter.ToUInt16 (tmp, 0, endian == Endian.Little);
747 public class Header {
748 public Endian endian;
750 private uint directory_offset;
751 public ImageDirectory Directory;
753 public Header (System.IO.Stream stream)
755 byte [] data = new byte [8];
756 stream.Read (data, 0, data.Length);
757 if (data [0] == 'M' && data [1] == 'M')
758 endian = Endian.Big;
759 else if (data [0] == 'I' && data [1] == 'I')
760 endian = Endian.Little;
762 ushort marker = EndianConverter.ToUInt16 (data, 2, endian == Endian.Little);
763 switch (marker) {
764 case 42:
765 //System.Console.WriteLine ("Found Standard Tiff Marker {0}", marker);
766 break;
767 case 0x4f52:
768 System.Console.WriteLine ("Found Olympus Tiff Marker {0}", marker.ToString ("x"));
769 break;
770 case 0x4e31:
771 System.Console.WriteLine ("Found Navy Interchange File Format Tiff Marker {0}", marker.ToString ("x"));
772 break;
773 default:
774 System.Console.WriteLine ("Found Unknown Tiff Marker {0}", marker.ToString ("x"));
775 break;
778 //System.Console.WriteLine ("Converting Something");
779 directory_offset = EndianConverter.ToUInt32 (data, 4, endian == Endian.Little);
781 if (directory_offset < 8)
782 throw new System.Exception ("Invalid IFD0 Offset [" + directory_offset.ToString () + "]");
784 //System.Console.WriteLine ("Reading First IFD");
785 Directory = new ImageDirectory (stream, directory_offset, endian);
789 #if INSIDE_FSPOT
790 public void Select (SemWeb.StatementSink sink)
792 SelectDirectory (Directory, sink);
795 public void SelectDirectory (ImageDirectory dir, StatementSink sink)
797 foreach (DirectoryEntry e in dir.Entries) {
798 //System.Console.WriteLine ("{0}", e.Id);
799 switch (e.Id) {
800 case TagId.IPTCNAA:
801 System.IO.Stream iptcstream = new System.IO.MemoryStream (e.RawData);
802 FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
803 iptc.Select (sink);
804 break;
805 case TagId.PhotoshopPrivate:
806 System.IO.Stream bimstream = new System.IO.MemoryStream (e.RawData);
807 FSpot.Bim.BimFile bim = new FSpot.Bim.BimFile (bimstream);
808 bim.Select (sink);
809 break;
810 case TagId.XMP:
811 System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
812 FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
813 xmp.Select (sink);
814 break;
815 case TagId.ImageDescription:
816 MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt",
817 new Literal (e.ValueAsString [0], "x-default", null));
818 break;
819 case TagId.UserComment:
820 MetadataStore.AddLiteral (sink, "exif:UserComment", "rdf:Alt",
821 new Literal (e.ValueAsString [0], "x-default", null));
822 break;
823 case TagId.Copyright:
824 MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt",
825 new Literal (e.ValueAsString [0], "x-default", null));
826 break;
827 case TagId.Artist:
828 MetadataStore.Add (sink, "dc:creator", "rdf:Seq", e.ValueAsString);
829 break;
830 case TagId.ExifIfdPointer:
831 try {
832 ImageDirectory sub = ((SubdirectoryEntry)e).Directory [0];
833 SelectDirectory (sub, sink);
834 } catch (System.Exception exc) {
835 System.Console.WriteLine (exc);
837 break;
838 case TagId.Software:
839 MetadataStore.AddLiteral (sink, "xmp:CreatorTool", e.ValueAsString [0]);
840 break;
841 case TagId.DateTime:
842 try {
844 MetadataStore.AddLiteral (sink, "xmp:ModifyDate",
845 e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
846 } catch (System.Exception ex) {
847 System.Console.WriteLine (String.Format ("error parsing {0}\n{1}", e.ValueAsString[0], ex));
850 break;
851 case TagId.DateTimeOriginal:
852 case TagId.DateTimeDigitized:
853 // FIXME subsectime needs to be included in these values
854 // FIXME shouldn't DateTimeOriginal be xmp:CreateDate? the spec says no but wtf?
855 try {
856 MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (),
857 e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
858 } catch (System.Exception ex) {
859 System.Console.WriteLine (String.Format ("error parsing {0}\n{1}", e.ValueAsString[0], ex));
861 break;
862 //case TagId.SpatialFrequencyResponse
863 case TagId.ExifCFAPattern:
864 CFAPattern pattern = new CFAPattern (e.RawData, e.IsLittle);
865 Entity empty = new Entity (null);
866 Statement top = new Statement ("",
867 (Entity)MetadataStore.Namespaces.Resolve ("exif:" + e.Id.ToString ()),
868 empty);
870 Statement cols = new Statement (empty,
871 (Entity) MetadataStore.Namespaces.Resolve ("exif:Columns"),
872 new Literal (pattern.Columns.ToString (), null, null));
873 sink.Add (cols);
874 Statement rows = new Statement (empty,
875 (Entity) MetadataStore.Namespaces.Resolve ("exif:Rows"),
876 new Literal (pattern.Rows.ToString (), null, null));
877 sink.Add (rows);
878 string [] vals = e.ArrayToString (pattern.Values);
879 MetadataStore.Add (sink, empty, "exif:Values", "rdf:Seq", vals);
880 sink.Add (top);
881 break;
882 case TagId.ExifVersion:
883 case TagId.FlashPixVersion:
884 case TagId.ColorSpace:
885 case TagId.CompressedBitsPerPixel:
886 case TagId.PixelYDimension:
887 case TagId.PixelXDimension:
888 case TagId.RelatedSoundFile:
889 case TagId.ExposureTime:
890 case TagId.FNumber:
891 case TagId.ExposureProgram:
892 case TagId.SpectralSensitivity:
893 case TagId.ShutterSpeedValue:
894 case TagId.ApertureValue:
895 case TagId.BrightnessValue:
896 case TagId.ExposureBiasValue:
897 case TagId.MaxApertureValue:
898 case TagId.SubjectDistance:
899 case TagId.MeteringMode:
900 case TagId.LightSource:
901 case TagId.FocalLength:
902 case TagId.FlashEnergy:
903 case TagId.FocalPlaneXResolution:
904 case TagId.FocalPlaneYResolution:
905 case TagId.FocalPlaneResolutionUnit:
906 case TagId.ExposureIndex:
907 case TagId.SensingMethod:
908 case TagId.FileSource:
909 case TagId.SceneType:
910 case TagId.CustomRendered:
911 case TagId.ExposureMode:
912 case TagId.WhiteBalance:
913 case TagId.DigitalZoomRatio:
914 case TagId.FocalLengthIn35mmFilm:
915 case TagId.SceneCaptureType:
916 case TagId.GainControl:
917 case TagId.Contrast:
918 case TagId.Saturation:
919 case TagId.Sharpness:
920 MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (), e.ValueAsString [0]);
921 break;
922 case TagId.ComponentsConfiguration:
923 case TagId.ISOSpeedRatings:
924 case TagId.SubjectArea:
925 case TagId.SubjectLocation:
926 MetadataStore.Add (sink, "exif:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
927 break;
928 case TagId.TransferFunction:
929 case TagId.YCbCrSubSampling:
930 case TagId.WhitePoint:
931 case TagId.PrimaryChromaticities:
932 case TagId.YCbCrCoefficients:
933 case TagId.ReferenceBlackWhite:
934 case TagId.BitsPerSample:
935 MetadataStore.Add (sink, "tiff:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
936 break;
937 case TagId.Orientation:
938 case TagId.Compression:
939 case TagId.PhotometricInterpretation:
940 case TagId.SamplesPerPixel:
941 case TagId.PlanarConfiguration:
942 case TagId.YCbCrPositioning:
943 case TagId.ResolutionUnit:
944 case TagId.ImageWidth:
945 case TagId.ImageLength:
946 case TagId.Model:
947 case TagId.Make:
948 MetadataStore.AddLiteral (sink, "tiff:" + e.Id.ToString (), e.ValueAsString [0]);
949 break;
953 #endif
955 public void Dump (string name)
957 ImageDirectory ifd = Directory;
958 for (int i = 0; ifd != null; i++) {
959 ifd.Dump (System.String.Format ("IFD[{0}]:", i));
960 ifd = ifd.NextDirectory;
965 public class ImageDirectory {
966 protected Endian endian;
967 protected ushort num_entries;
968 protected System.Collections.ArrayList entries;
969 protected uint orig_position;
971 protected uint next_directory_offset;
972 ImageDirectory next_directory;
974 protected bool has_header;
975 protected bool has_footer;
977 public ImageDirectory (System.IO.Stream stream, uint start_position, Endian endian)
979 this.endian = endian;
980 orig_position = start_position;
981 Load (stream);
984 protected void Load (System.IO.Stream stream)
986 ReadHeader (stream);
987 ReadEntries (stream);
988 ReadFooter (stream);
990 LoadEntries (stream);
991 LoadNextDirectory (stream);
994 public virtual bool ReadHeader (System.IO.Stream stream)
996 stream.Seek ((long)orig_position, System.IO.SeekOrigin.Begin);
997 return true;
1000 protected virtual void ReadEntries (System.IO.Stream stream)
1002 num_entries = Converter.ReadUShort (stream, endian);
1003 //System.Console.WriteLine ("reading {0} entries", num_entries);
1005 entries = new System.Collections.ArrayList (num_entries);
1006 int entry_length = num_entries * 12;
1007 byte [] content = new byte [entry_length];
1009 if (stream.Read (content, 0, content.Length) < content.Length)
1010 throw new System.Exception ("Short Read");
1012 for (int pos = 0; pos < entry_length; pos += 12) {
1013 DirectoryEntry entry = EntryFactory.CreateEntry (this, content, pos, this.endian);
1014 entries.Add (entry);
1015 //System.Console.WriteLine ("Added Entry {0} {1} - {2} * {3}", entry.Id.ToString (), entry.Id.ToString ("x"), entry.Type, entry.Count);
1016 if (entry.Id == TagId.NewSubfileType) {
1022 protected virtual void ReadFooter (System.IO.Stream stream)
1024 next_directory_offset = Converter.ReadUInt (stream, this.endian);
1027 protected void LoadEntries (System.IO.Stream stream)
1029 foreach (DirectoryEntry entry in entries) {
1030 entry.LoadExternal (stream);
1034 protected void LoadNextDirectory (System.IO.Stream stream)
1036 //System.Console.WriteLine ("next_directory_offset = {0}", next_directory_offset);
1037 next_directory = null;
1038 try {
1039 if (next_directory_offset != 0)
1040 next_directory = new ImageDirectory (stream, next_directory_offset, this.endian);
1042 } catch {
1043 //System.Console.WriteLine ("Error loading directory {0}", e.ToString ());
1044 next_directory = null;
1045 next_directory_offset = 0;
1049 public ImageDirectory NextDirectory {
1050 get {
1051 return next_directory;
1055 public System.Collections.ArrayList Entries {
1056 get {
1057 return entries;
1061 public DirectoryEntry Lookup (TagId id)
1063 foreach (DirectoryEntry entry in entries)
1064 if (entry.Id == id)
1065 return entry;
1067 return null;
1071 public DirectoryEntry Lookup (uint id)
1073 foreach (DirectoryEntry entry in entries)
1074 if ((uint)entry.Id == id)
1075 return entry;
1077 return null;
1080 public void Dump (string name)
1082 System.Console.WriteLine ("Starting {0}", name);
1083 foreach (DirectoryEntry e in this.Entries)
1084 e.Dump (name);
1085 System.Console.WriteLine ("Ending {0}", name);
1088 public string Dump2 ()
1090 System.Text.StringBuilder builder = new System.Text.StringBuilder ();
1091 builder.Append ("Dummping IFD");
1092 foreach (DirectoryEntry entry in entries) {
1093 builder.Append (entry.ToString ()+ "\n");
1095 if (entry is SubdirectoryEntry)
1096 builder.Append ("Found SUBDIRECTORYENTRY\n");
1099 if (next_directory != null) {
1100 builder.Append ("Dummping Next IFD");
1101 builder.Append (next_directory.Dump2 ());
1104 return builder.ToString ();
1108 public class EntryFactory {
1109 //public delegate DirectoryEntry ConstructorFunc (byte [], Endian endian);
1110 //public static System.Collections.Hashtable ctors = new System.Collections.Hashtable ();
1112 public static DirectoryEntry CreateEntry (ImageDirectory parent, byte [] input, int start, Endian header_endian)
1114 TagId tagid;
1115 EntryType type;
1117 DirectoryEntry.ParseHeader (input, start, out tagid, out type, header_endian);
1118 //ConstructorFunc ctor = ctors[tagid];
1119 //if (ctor == null) {
1120 // return ctor (input, header_endian);
1123 switch (tagid) {
1124 case TagId.ExifIfdPointer:
1125 case TagId.GPSInfoIfdPointer:
1126 case TagId.InteroperabilityIfdPointer:
1127 case TagId.SubIFDs:
1128 return new SubdirectoryEntry (input, start, header_endian);
1129 //case TagId.MakerNote:
1130 //return new MakerNoteEntry (input, start, header_endian);
1131 //case TagId.PimIfdPointer:
1132 //return new
1133 //case TagId.MakerNote:
1134 //return new MakerNoteEntry (input, start, header_endian);
1137 switch (type) {
1138 case EntryType.Ifd:
1139 //System.Console.WriteLine ("Trying to load {0} {1}", tagid, tagid.ToString ("x"));
1140 return new SubdirectoryEntry (input, start, header_endian);
1141 case EntryType.Byte:
1142 return new ByteEntry (input, start, header_endian);
1143 case EntryType.Long:
1144 return new LongEntry (input, start, header_endian);
1145 case EntryType.Short:
1146 return new ShortEntry (input, start, header_endian);
1149 return new DirectoryEntry (input, start, header_endian);
1153 public class MakerNoteEntry : SubdirectoryEntry {
1154 public MakerNoteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1159 public override uint GetEntryCount ()
1161 return 1;
1164 public override void LoadExternal (System.IO.Stream stream)
1170 public class SubdirectoryEntry : DirectoryEntry {
1171 public uint directory_offset;
1172 public ImageDirectory [] Directory;
1174 public SubdirectoryEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1176 if (this.GetEntryCount () > 1) {
1177 System.Console.WriteLine ("Count is greater than 1 ({1}) on Subdirectory {0} interesting", tagid, count);
1181 public virtual uint GetEntryCount ()
1183 return count;
1186 public override void LoadExternal (System.IO.Stream stream)
1188 uint entry_count = GetEntryCount ();
1189 Directory = new ImageDirectory [entry_count];
1191 base.LoadExternal (stream);
1193 for (int i = 0; i < entry_count; i++) {
1194 try {
1195 directory_offset = EndianConverter.ToUInt32 (raw_data, i * 4, endian == Endian.Little);
1196 Directory [i] = new ImageDirectory (stream, directory_offset, endian);
1197 } catch (System.Exception e) {
1198 System.Console.WriteLine ("Error loading Subdirectory {0} at {2} of {3}bytes:\n{1}",
1199 this.Id, e, directory_offset, stream.Length);
1205 public override void Dump (string name)
1207 for (int i = 0; i < GetEntryCount (); i++) {
1208 string subdirname = System.String.Format ("{0}{1}[{2}]({3})]", name, tagid, i, directory_offset);
1210 try {
1211 if (Directory [i] != null)
1212 Directory [i].Dump (subdirname);
1213 } catch (System.Exception e) {
1214 System.Console.WriteLine (e);
1220 public class ShortEntry : DirectoryEntry {
1221 public ShortEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1226 public class LongEntry : DirectoryEntry {
1227 public LongEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1229 if (type != EntryType.Long)
1230 throw new System.Exception (System.String.Format ("Invalid Settings At Birth {0}", tagid));
1234 public class ByteEntry : DirectoryEntry {
1235 public ByteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
1237 if (type != EntryType.Byte)
1238 throw new System.Exception ("Invalid Settings At Birth");
1242 #if false
1243 public class ImageLoader {
1244 int width;
1245 int length;
1246 int [] bps;
1247 PhotometricInterpretation interpretation;
1248 Compression compression;
1249 uint [] offsets;
1250 uint [] strip_byte_counts;
1251 uint rows_per_strip;
1252 byte [] strip;
1254 public ImageLoader (ImageDirectory directory)
1256 width = directory.Lookup (TagId.ImageWidth).ValueAsLong [0];
1257 length = directory.Lookup (TagId.ImageLength).ValueAsLong [0];
1258 bps = directory.Lookup (TagId.BitsPerSample).ValueAsLong;
1260 compression = (Compression) directory.Lookup (TagId.Compression).ValueAsLong [0];
1261 interpretation = (PhotometricInterpretation) directory.Lookup (TagId.PhotometricInterpretation).ValueAsLong [0];
1263 offsets = directory.Lookup (TagId.StripOffsets).ValueAsLong;
1264 strip_byte_counts = directory.Lookup (TagId.StripByteCounts).ValueAsLong;
1265 rows_per_strip = directory.Lookup (TagId.RowsPerStrip).ValueAsLong [0];
1267 if (interpretation !=
1271 public Gdk.Pixbuf LoadPixbuf (System.IO.Stream stream)
1273 Gdk.Pixbuf dest = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, width, height);
1274 strip = new byte [strip_byte_counts];
1275 int row;
1276 for (int i = 0; i < offsets.Length; i++) {
1277 strip = new byte [strip_byte_counts [i]];
1278 stream.Read (strip, 0, strip.Length);
1279 switch (compression) {
1280 case Compression.Notice
1286 #endif
1288 public class DirectoryEntry {
1289 protected TagId tagid;
1290 protected EntryType type;
1291 protected uint count;
1292 protected uint offset_origin;
1293 protected uint data_offset;
1295 protected byte [] raw_data;
1296 protected Endian endian;
1298 public TagId Id {
1299 get {
1300 return tagid;
1304 public EntryType Type {
1305 get {
1306 return type;
1310 public uint Count {
1311 get {
1312 return count;
1316 public void SetOrigin (uint pos)
1318 offset_origin = pos;
1321 public uint Position {
1322 get {
1323 return offset_origin + data_offset;
1327 public virtual int GetTypeSize ()
1329 return GetTypeSize (type);
1332 public static int GetTypeSize (EntryType type)
1334 switch (type) {
1335 case EntryType.Byte:
1336 case EntryType.SByte:
1337 case EntryType.Undefined:
1338 case EntryType.Ascii:
1339 return 1;
1340 case EntryType.Short:
1341 case EntryType.SShort:
1342 return 2;
1343 case EntryType.Long:
1344 case EntryType.SLong:
1345 case EntryType.Float:
1346 return 4;
1347 case EntryType.Double:
1348 case EntryType.Rational:
1349 case EntryType.SRational:
1350 return 8;
1351 default:
1352 return 1;
1356 public bool IsLittle {
1357 get {
1358 return (endian == Endian.Little);
1362 public static int ParseHeader (byte [] data, int start, out TagId tagid, out EntryType type, Endian endian)
1364 tagid = (TagId) EndianConverter.ToUInt16 (data, start, endian == Endian.Little);
1365 type = (EntryType) EndianConverter.ToUInt16 (data, start + 2, endian == Endian.Little);
1366 return 4;
1369 public DirectoryEntry (byte [] data, int start, Endian endian)
1371 this.endian = endian;
1373 start += ParseHeader (data, start, out this.tagid, out this.type, endian);
1374 ParseStream (data, start);
1377 public virtual void LoadExternal (System.IO.Stream stream)
1379 if (data_offset != 0) {
1380 stream.Seek ((long)Position, System.IO.SeekOrigin.Begin);
1381 byte [] data = new byte [count * GetTypeSize ()];
1382 if (stream.Read (data, 0, data.Length) < data.Length)
1383 throw new System.Exception ("Short Read");
1384 raw_data = data;
1387 #if false
1388 switch ((int)this.Id) {
1389 case (int)TagId.NewSubfileType:
1390 System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new NewSubFileType {0}", (NewSubfileType) this.ValueAsLong [0]);
1391 break;
1392 case (int)TagId.SubfileType:
1393 System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new SubFileType {0}", (SubfileType) this.ValueAsLong [0]);
1394 break;
1395 case (int)TagId.Compression:
1396 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new Compression {0}", (Compression) this.ValueAsLong [0]);
1398 break;
1399 case (int)TagId.JPEGProc:
1400 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new JPEGProc {0}", (JPEGProc) this.ValueAsLong [0]);
1402 break;
1403 case (int)TagId.PhotometricInterpretation:
1404 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new PhotometricInterpretation {0}", (PhotometricInterpretation) this.ValueAsLong [0]);
1405 break;
1406 case (int)TagId.ImageWidth:
1407 case (int)TagId.ImageLength:
1408 //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new {1} {0}", this.ValueAsLong [0], this.Id);
1409 break;
1410 case 50648:
1411 case 50656:
1412 case 50752:
1413 System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX {0}({1}) - {2} {3}", this.Id, this.Id.ToString ("x"), this.type, raw_data.Length);
1414 System.Console.WriteLine ("XXXX ", System.Text.Encoding.ASCII.GetString (raw_data));
1415 switch (this.type) {
1416 case EntryType.Long:
1417 foreach (uint val in ((LongEntry)this).Value)
1418 System.Console.Write (" {0}", val);
1419 break;
1420 case EntryType.Short:
1421 foreach (ushort val in ((ShortEntry)this).ShortValue)
1422 System.Console.Write (" {0}", val);
1423 break;
1424 case EntryType.Byte:
1425 foreach (byte val in this.RawData)
1426 System.Console.Write (" {0}", val);
1427 break;
1429 System.Console.WriteLine ("");
1430 break;
1432 #endif
1435 public virtual void Dump (string name)
1437 switch (this.Type) {
1438 case EntryType.Short:
1439 case EntryType.Long:
1440 uint [] vals = this.ValueAsLong;
1441 System.Console.Write ("{3}{1}({2}) [{0}] (", vals.Length, this.Id, this.Type, name);
1442 for (int i = 0; i < System.Math.Min (15, vals.Length); i++) {
1443 System.Console.Write (" {0}", vals [i]);
1445 System.Console.WriteLine (")");
1446 break;
1447 case EntryType.Ascii:
1448 System.Console.WriteLine ("{3}{1}({2}) (\"{0}\")", this.StringValue, this.Id, this.Type, name);
1449 break;
1450 default:
1451 System.Console.WriteLine ("{3}{1}({2}) [{0}]", this.Count, this.Id, this.Type, name);
1452 break;
1456 protected void ParseStream (byte [] data, int start)
1458 int i = start;
1460 count = EndianConverter.ToUInt32 (data, i, endian == Endian.Little);
1461 i += 4;
1462 int size = (int)count * GetTypeSize ();
1463 if (size > 4)
1464 data_offset = EndianConverter.ToUInt32 (data, i, endian == Endian.Little);
1465 else {
1466 data_offset = 0;
1467 raw_data = new byte [size];
1468 System.Array.Copy (data, i, raw_data, 0, size);
1472 public void SetData (string value)
1474 int len = System.Text.Encoding.UTF8.GetByteCount (value);
1475 byte [] tmp = new byte [len + 1];
1476 System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
1477 tmp[len] = 0;
1478 System.Console.WriteLine ("SetData: value = {0} len = {1}", value, len);
1479 SetData (tmp);
1482 public static System.DateTime DateTimeFromString (string dt)
1484 // Exif DateTime strings are formatted as
1485 // "YYYY:MM:DD HH:MM:SS"
1487 string delimiters = " :";
1488 string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
1489 System.DateTime result;
1490 result = new System.DateTime (System.Int32.Parse(dt_data[0]),
1491 System.Int32.Parse(dt_data[1]),
1492 System.Int32.Parse(dt_data[2]),
1493 System.Int32.Parse(dt_data[3]),
1494 System.Int32.Parse(dt_data[4]),
1495 System.Int32.Parse(dt_data[5]));
1497 return result;
1500 public void SetData (byte [] data)
1502 raw_data = data;
1503 count = (uint)raw_data.Length / (uint)GetTypeSize ();
1506 #if false
1507 public object GetValue () {
1508 switch (Type) {
1509 case EntryType.Short:
1510 return ShortValue;
1511 case EntryType.Long:
1512 return LongValue;
1513 case EntryType.Rational:
1514 return RationalValue;
1515 case EntryType.SRational:
1516 return SRationalValue;
1517 case EntryType.Ascii:
1518 return StringValue.Split ('\0');
1519 break;
1520 default:
1521 System.Console.WriteLine ("{1}({2}) [{0}]", this.Count, this.Id, this.Type);
1522 break;
1527 #endif
1529 public byte [] Value {
1530 get {
1531 return raw_data;
1535 public byte [] RawData {
1536 get {
1537 return raw_data;
1541 public string [] ValueAsString {
1542 get {
1543 switch (this.Type) {
1544 case EntryType.Short:
1545 case EntryType.Long:
1546 return ArrayToString (this.ValueAsLong);
1547 case EntryType.Rational:
1548 return ArrayToString (this.RationalValue);
1549 case EntryType.SRational:
1550 return ArrayToString (this.SRationalValue);
1551 case EntryType.Undefined:
1552 switch (Id) {
1553 case TagId.UserComment:
1554 return new string [] { UserCommentValue };
1555 case TagId.FlashPixVersion:
1556 case TagId.ExifVersion:
1557 return new string [] { StringValue };
1558 case TagId.FileSource:
1559 case TagId.SceneType:
1560 return ArrayToString (this.RawData);
1561 case TagId.ComponentsConfiguration:
1562 return ArrayToString (ValueAsLong);
1563 default:
1564 System.Console.WriteLine ("Cannot convert type \"{0}\" to string", Id);
1565 break;
1567 break;
1568 case EntryType.Ascii:
1569 return StringValue.Split ('\0');
1571 return null;
1575 public string [] ArrayToString (System.Array array)
1577 string [] vals = new string [array.Length];
1578 for (int i = 0; i < array.Length; i++)
1579 vals [i] = array.GetValue (i).ToString ();
1581 return vals;
1584 public uint [] ValueAsLong {
1585 get {
1586 uint [] data = new uint [this.Count];
1587 for (int i = 0; i < this.Count; i++) {
1588 switch (this.Type) {
1589 case EntryType.Long:
1590 data [i] = EndianConverter.ToUInt32 (raw_data, i * GetTypeSize (), endian == Endian.Little);
1591 break;
1592 case EntryType.Short:
1593 data [i] = EndianConverter.ToUInt16 (raw_data, i * GetTypeSize (), endian == Endian.Little);
1594 break;
1595 case EntryType.Undefined:
1596 case EntryType.Byte:
1597 data [i] = raw_data [i];
1598 break;
1599 default:
1600 throw new System.Exception ("Invalid conversion");
1603 return data;
1607 // The following methods are usded to convert the data
1608 // to the various type regardless of the entry
1609 // type, they are used internally in processing the data
1610 // Use at your own risk.
1612 public string StringValue {
1613 get {
1614 return System.Text.Encoding.ASCII.GetString (raw_data);
1618 public System.DateTime ValueAsDate {
1619 get {
1620 return DirectoryEntry.DateTimeFromString (StringValue);
1624 public string UserCommentValue {
1625 get {
1626 UserComment comment = new UserComment (raw_data, IsLittle);
1627 return comment.Value;
1631 public SRational [] SRationalValue {
1632 get {
1633 Rational [] vals = RationalValue;
1634 SRational [] data = new SRational [vals.Length];
1636 for (int i = 0; i < vals.Length; i++)
1637 data [i] = SRational.BitwiseCopy (vals [i]);
1639 return data;
1643 public Rational [] RationalValue {
1644 get {
1645 uint [] vals = LongValue;
1646 Rational [] data = new Rational [vals.Length / 2];
1648 for (int i = 0; i < vals.Length; i += 2)
1649 data [i/2] = new Rational (vals [i], vals [i + 1]);
1651 return data;
1656 public uint [] LongValue {
1657 get {
1658 uint [] data = new uint [raw_data.Length / 4];
1659 for (int i = 0; i < raw_data.Length; i+= 4)
1660 data [i/4] = EndianConverter.ToUInt32 (raw_data, i, endian == Endian.Little);
1662 return data;
1666 public ushort [] ShortValue {
1667 get {
1668 ushort [] data = new ushort [raw_data.Length];
1669 for (int i = 0; i < raw_data.Length; i+= 2) {
1670 data [i] = EndianConverter.ToUInt16 (raw_data, i, endian == Endian.Little);
1672 return data;
1677 #if false
1678 public class TiffFile : ImageFile, SemWeb.StatementSource {
1679 public Header Header;
1681 public TiffFile (string path) : base (path)
1683 try {
1684 using (System.IO.Stream input = System.IO.File.OpenRead (path)) {
1685 this.Header = new Header (input);
1688 #if DEBUG_LOADER
1689 Header.Dump (this.ToSring () + ":");
1690 #endif
1691 } catch (System.Exception e) {
1692 System.Console.WriteLine (e.ToString ());
1696 #if INSIDE_FSPOT
1697 public virtual void Select (SemWeb.StatementSink sink)
1699 Header.SelectDirectory (Header.Directory, sink);
1701 #endif
1702 public override System.DateTime Date {
1703 get {
1704 SubdirectoryEntry sub = (SubdirectoryEntry) this.Header.Directory.Lookup (TagId.ExifIfdPointer);
1705 DirectoryEntry e;
1707 if (sub != null) {
1708 e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
1710 if (e != null)
1711 return DirectoryEntry.DateTimeFromString (e.StringValue);
1714 e = this.Header.Directory.Lookup (TagId.DateTime);
1716 if (e != null)
1717 return DirectoryEntry.DateTimeFromString (e.StringValue);
1718 else
1719 return base.Date;
1723 public override System.IO.Stream PixbufStream ()
1725 return null;
1728 public override PixbufOrientation GetOrientation ()
1730 ShortEntry e = (ShortEntry)(this.Header.Directory.Lookup (TagId.Orientation));
1731 if (e != null)
1732 return (PixbufOrientation)(e.ShortValue[0]);
1733 else
1734 return PixbufOrientation.TopLeft;
1737 public System.IO.Stream LookupJpegSubstream (ImageDirectory directory)
1739 uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
1741 System.IO.Stream file = System.IO.File.OpenRead (this.path);
1742 file.Position = offset;
1743 return file;
1746 public Gdk.Pixbuf LoadJpegInterchangeFormat (ImageDirectory directory)
1748 uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
1749 uint length = directory.Lookup (TagId.JPEGInterchangeFormatLength).ValueAsLong [0];
1751 using (System.IO.Stream file = System.IO.File.OpenRead (this.path)) {
1752 file.Position = offset;
1754 byte [] data = new byte [32768];
1755 int read;
1757 Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
1759 while (length > 0) {
1760 read = file.Read (data, 0, (int)System.Math.Min ((int)data.Length, length));
1761 if (read <= 0)
1762 break;
1764 loader.Write (data, (ulong)read);
1765 length -= (uint) read;
1767 Gdk.Pixbuf result = loader.Pixbuf;
1768 loader.Close ();
1769 return result;
1774 public class DngFile : TiffFile {
1775 public DngFile (string path) : base (path)
1779 public override System.IO.Stream PixbufStream ()
1781 try {
1782 SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1783 ImageDirectory directory = sub.Directory [sub.Directory.Length - 1];
1785 uint offset = directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
1786 System.IO.Stream file = System.IO.File.OpenRead (this.path);
1787 file.Position = offset;
1788 return file;
1789 } catch {
1790 return DCRawFile.RawPixbufStream (path);
1794 public override Gdk.Pixbuf Load ()
1796 return DCRawFile.Load (this.path, null);
1799 public override void Select (SemWeb.StatementSink sink)
1802 /* this is just a sanity pass, if the first ifd is not a subfile use the normal
1803 * tiff path
1805 DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
1806 if (e == null) {
1807 base.Select (sink);
1808 return;
1812 * Even though Ifd0 doesn't have the full resolution image
1813 * it would have the XMP data so we look for it
1815 e = Header.Directory.Lookup (TagId.XMP);
1816 if (e != null) {
1817 System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
1818 FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
1819 xmp.Select (sink);
1823 * Ifd0 will also have the exif directory
1825 ImageDirectory dir = Header.Directory;
1826 SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
1827 if (sub != null)
1828 Header.SelectDirectory (sub.Directory [0], sink);
1831 * now we lookup subifd0 (we should probably scan the newsubfile types here)
1832 * and load the metadata we are interested in from it.
1834 sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1836 int i = 0;
1837 do {
1838 uint dirtype = e.ValueAsLong [0];
1839 if (dirtype == 0) {
1840 Header.SelectDirectory (dir, sink);
1841 break;
1844 if (sub == null)
1845 break;
1847 dir = sub.Directory [i];
1848 e = dir.Lookup (TagId.NewSubfileType);
1849 i++;
1850 } while (i < sub.Directory.Length);
1856 public class NefFile : TiffFile, IThumbnailContainer {
1857 public NefFile (string path) : base (path)
1861 public override void Select (SemWeb.StatementSink sink)
1863 DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
1865 if (e == null) {
1866 base.Select (sink);
1867 return;
1870 ImageDirectory dir = Header.Directory;
1871 SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
1873 if (sub != null)
1874 Header.SelectDirectory (sub.Directory [0], sink);
1876 sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1878 int i = 0;
1879 do {
1880 uint dirtype = e.ValueAsLong [0];
1881 if (dirtype == 0) {
1882 Header.SelectDirectory (dir, sink);
1883 break;
1886 if (sub == null)
1887 break;
1889 dir = sub.Directory [i];
1890 e = dir.Lookup (TagId.NewSubfileType);
1891 i++;
1892 } while (i < sub.Directory.Length);
1895 public Gdk.Pixbuf GetEmbeddedThumbnail ()
1897 return TransformAndDispose (new Gdk.Pixbuf (path));
1900 public override System.IO.Stream PixbufStream ()
1902 try {
1903 SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1904 ImageDirectory jpeg_directory = sub.Directory [0];
1905 return LookupJpegSubstream (jpeg_directory);
1906 } catch (System.Exception e) {
1907 return DCRawFile.RawPixbufStream (path);
1911 public override Gdk.Pixbuf Load ()
1913 Gdk.Pixbuf pixbuf = null;
1914 System.Console.WriteLine ("starting load");
1916 try {
1917 SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
1918 ImageDirectory jpeg_directory = sub.Directory [0];
1920 pixbuf = LoadJpegInterchangeFormat (jpeg_directory);
1921 } catch (System.Exception e) {
1922 System.Console.WriteLine (e);
1923 pixbuf = null;
1926 if (pixbuf == null)
1927 return DCRawFile.Load (this.Path, null);
1929 return TransformAndDispose (pixbuf);
1934 public class Cr2File : TiffFile, IThumbnailContainer {
1936 public Cr2File (string path) : base (path)
1941 public override PixbufOrientation GetOrientation ()
1943 return PixbufOrientation.TopLeft;
1947 public Gdk.Pixbuf GetEmbeddedThumbnail ()
1949 ImageDirectory directory;
1950 directory = Header.Directory.NextDirectory;
1951 return TransformAndDispose (LoadJpegInterchangeFormat (directory));
1955 public override System.IO.Stream PixbufStream ()
1957 uint offset = Header.Directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
1958 System.IO.Stream file = System.IO.File.OpenRead (this.path);
1959 file.Position = offset;
1960 return file;
1961 //return LookupJpegSubstream (Header.Directory.NextDirectory);
1962 //return DCRawFile.RawPixbufStream (path);
1965 public override Gdk.Pixbuf Load ()
1967 return DCRawFile.Load (this.Path, null);
1970 public override System.DateTime Date
1972 get {
1973 SubdirectoryEntry sub = (SubdirectoryEntry) this.Header.Directory.Lookup (TagId.ExifIfdPointer);
1974 DirectoryEntry e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
1976 if (e != null)
1977 return DirectoryEntry.DateTimeFromString (e.StringValue);
1978 else
1979 return base.Date;
1983 #endif