beagle r4696 - in trunk/beagle/Util/F-Spot: . Imaging upstream-changes
- From: dbera svn gnome org
- To: svn-commits-list gnome org
- Subject: beagle r4696 - in trunk/beagle/Util/F-Spot: . Imaging upstream-changes
- Date: Sun, 13 Apr 2008 20:18:35 +0100 (BST)
Author: dbera
Date: Sun Apr 13 20:18:35 2008
New Revision: 4696
URL: http://svn.gnome.org/viewvc/beagle?rev=4696&view=rev
Log:
Import F-Spot files from r3823.
Added:
trunk/beagle/Util/F-Spot/
trunk/beagle/Util/F-Spot/BitConverter.cs
trunk/beagle/Util/F-Spot/Imaging/
trunk/beagle/Util/F-Spot/Imaging/Bim.cs
trunk/beagle/Util/F-Spot/Imaging/Ciff.cs
trunk/beagle/Util/F-Spot/Imaging/DCRawFile.cs
trunk/beagle/Util/F-Spot/Imaging/Exif.cs
trunk/beagle/Util/F-Spot/Imaging/ImageFile.cs
trunk/beagle/Util/F-Spot/Imaging/IptcFile.cs
trunk/beagle/Util/F-Spot/Imaging/JpegFile.cs
trunk/beagle/Util/F-Spot/Imaging/JpegHeader.cs
trunk/beagle/Util/F-Spot/Imaging/JpegUtils.cs
trunk/beagle/Util/F-Spot/Imaging/MrwFile.cs
trunk/beagle/Util/F-Spot/Imaging/OrderedWriter.cs
trunk/beagle/Util/F-Spot/Imaging/PngFile.cs
trunk/beagle/Util/F-Spot/Imaging/PnmFile.cs
trunk/beagle/Util/F-Spot/Imaging/RafFile.cs
trunk/beagle/Util/F-Spot/Imaging/SvgFile.cs
trunk/beagle/Util/F-Spot/Imaging/Tiff.cs
trunk/beagle/Util/F-Spot/Imaging/X3fFile.cs
trunk/beagle/Util/F-Spot/Imaging/XmpFile.cs
trunk/beagle/Util/F-Spot/MetadataStore.cs
trunk/beagle/Util/F-Spot/PixbufUtils.cs
trunk/beagle/Util/F-Spot/README
trunk/beagle/Util/F-Spot/upstream-changes/
trunk/beagle/Util/F-Spot/upstream-changes/01-initial-diff-r3823.diff
Added: trunk/beagle/Util/F-Spot/BitConverter.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/BitConverter.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,94 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace FSpot {
+ public class BitConverter {
+ public static uint Swap (uint val, bool little)
+ {
+ return (little != System.BitConverter.IsLittleEndian) ?
+ ((uint) ((((uint) (val) & (uint) 0x000000ffU) << 24) |
+ (((uint) (val) & (uint) 0x0000ff00U) << 8) |
+ (((uint) (val) & (uint) 0x00ff0000U) >> 8) |
+ (((uint) (val) & (uint) 0xff000000U) >> 24)))
+ : val;
+ }
+
+ public static ushort Swap (ushort val, bool little)
+ {
+ return (little != System.BitConverter.IsLittleEndian) ?
+ ((ushort) ((ushort)(val >> 8) | (ushort)(val << 8)))
+ : val;
+ }
+
+ public static ushort Swap (ushort val)
+ {
+ return ((ushort) ((ushort)(val >> 8) | (ushort)(val << 8)));
+ }
+
+ public static ulong Swap (ulong val, bool little)
+ {
+ return (little != System.BitConverter.IsLittleEndian) ?
+ ((ulong) ((((ulong) (val) & (ulong) 0x00000000000000ffU) << 56) |
+ (((ulong) (val) & (ulong) 0x000000000000ff00U) << 40) |
+ (((ulong) (val) & (ulong) 0x0000000000ff0000U) << 24) |
+ (((ulong) (val) & (ulong) 0x00000000ff000000U) << 8) |
+ (((ulong) (val) & (ulong) 0x000000ff00000000U) >> 8) |
+ (((ulong) (val) & (ulong) 0x0000ff0000000000U) >> 24) |
+ (((ulong) (val) & (ulong) 0x00ff000000000000U) >> 40) |
+ (((ulong) (val) & (ulong) 0xff00000000000000U) >> 56)))
+ : val;
+ }
+
+ public static byte [] GetBytes (uint val, bool little)
+ {
+ val = Swap (val, little);
+ return System.BitConverter.GetBytes (val);
+ }
+
+ public static byte [] GetBytes (ushort val, bool little)
+ {
+ val = Swap (val, little);
+ return System.BitConverter.GetBytes (val);
+ }
+
+ public static byte [] GetBytes (ulong val, bool little)
+ {
+ val = Swap (val, little);
+ return System.BitConverter.GetBytes (val);
+ }
+
+ public static ushort ToUInt16 (byte [] data, int position, bool little)
+ {
+ ushort val = System.BitConverter.ToUInt16 (data, position);
+ return Swap (val, little);
+ }
+
+ public static uint ToUInt32 (byte [] data, int position, bool little)
+ {
+ uint val = System.BitConverter.ToUInt32 (data, position);
+ return Swap (val, little);
+ }
+
+ public static float ToSingle (byte [] data, int position, bool little)
+ {
+ float retval;
+ unsafe {
+ uint * ptr;
+ ptr = (uint *)&retval;
+ *ptr = ToUInt32 (data, position, little);
+ }
+ return retval;
+ }
+
+ public static int ToInt32 (byte [] data, int position, bool little)
+ {
+ return unchecked ((int) ToUInt32 (data, position, little));
+ }
+
+ public static ulong ToUInt64 (byte [] data, int position, bool little)
+ {
+ ulong val = System.BitConverter.ToUInt64(data, position);
+ return Swap (val, little);
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/Bim.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/Bim.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,212 @@
+namespace FSpot.Bim {
+ public enum EntryType : ushort {
+ ObsoleteImageInfo = 0x03e8,
+ MacPrintManager = 0x03e9,
+ MacPrintXML = 0x03ea,
+ ObsoleteIndexedColorTable = 0x03eb,
+ ResolutionInfo = 0x03ed,
+ AlphaChannelNames = 0x03ee,
+ DisplayInfo = 0x03ef,
+ Caption = 0x03f0,
+ Border = 0x03f1,
+ BackgroundColor = 0x03f2,
+ PrintFlags = 0x03f3,
+ GrayHalftone = 0x03f4,
+ ColorHalftone = 0x03f5,
+ DuotoneHalftone = 0x03f6,
+ GrayTransfer = 0x03f7,
+ ColorTransfer = 0x03f8,
+ DuotoneTransfer = 0x03f9,
+ DuotoneInformation = 0x03fa,
+ BWDot = 0x03fb,
+ ObsoleteUnknown1 = 0x03fc,
+ LayerState = 0x0400,
+ WorkingPath = 0x0401,
+ LayerGroupInfomation = 0x0402,
+ ObsoleteUnknown2 = 0x0403,
+ IPTCNAA = 0x0404,
+ RawImageMode = 0x0405,
+ JpegQuality = 0x0406,
+ GridGuideInformation = 0x0408,
+ ThumbnailResource = 0x0409,
+ CopyrightFlag = 0x040a,
+ URL = 0x040b,
+ ThumbnailResource2 = 0x040c,
+ GlobalAngle = 0x040d,
+ ColorSamplers = 0x040e,
+ ICCProfile = 0x040f,
+ Watermark = 0x0410,
+ ICCUntagged = 0x0411,
+ EffectsVisible = 0x0412,
+ SpotHalftone = 0x0413,
+ LastID = 0x0414,
+ UnicodeAlphaNames = 0x0415,
+ IndexedColorTable = 0x0416,
+ TransparentIndex = 0x0417,
+ GlobalAltitude = 0x0419,
+ Slices = 0x041a,
+ WorkflowURL = 0x041b,
+ XPEPJump = 0x041c,
+ AlphaIdentifiers = 0x041d,
+ URLList = 0x041e,
+ VersionInfo = 0x0421,
+ XMP = 0x0424,
+ FirstPath = 0x07d0,
+ LastPAth = 0x0bb6,
+ ClippingPathName = 0x0bb7,
+ PrintFlags2 = 0x2710,
+ }
+
+ /*
+ From what I can see it looks like these resources are a just
+ a list of records starting with a 8Bim\0 followed by a 16 bit
+ value that is the record type then a 8 bit offset and 32bit length. IPTC data is
+ type 0x0404, I don't know any other types at the moment.
+
+ see http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf and the section on image resource blocks.
+ */
+
+ public class Entry
+ {
+ public ushort Type;
+ public string Name;
+ public byte [] Data;
+
+ const string Marker = "8BIM";
+ public Entry ()
+ {
+
+ }
+
+ public int Load (System.IO.Stream stream)
+ {
+ byte [] header = new byte [6];
+
+ stream.Read (header, 0, header.Length);
+ if (System.Text.Encoding.ASCII.GetString (header, 0, 4) != Marker)
+ throw new System.Exception ("missing header");
+
+ Type = FSpot.BitConverter.ToUInt16 (header, 4, false);
+
+ int name_length = stream.ReadByte ();
+ if (name_length > 0) {
+ byte [] name_data = new byte [name_length];
+ stream.Read (name_data, 0, name_length);
+ Name = System.Text.Encoding.ASCII.GetString (name_data);
+ }
+
+ if (name_length % 2 == 0)
+ stream.ReadByte ();
+
+ stream.Read (header, 0, 4);
+ uint length = FSpot.BitConverter.ToUInt32 (header, 0, false);
+
+ Data = new byte [length];
+ stream.Read (Data, 0, Data.Length);
+
+ if (Data.Length % 2 > 0)
+ stream.ReadByte ();
+
+ return header.Length + Data.Length;
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ byte [] tmp;
+ tmp = System.Text.Encoding.ASCII.GetBytes (Marker);
+ stream.Write (tmp, 0, tmp.Length);
+ tmp = FSpot.BitConverter.GetBytes (Type, false);
+ stream.Write (tmp, 0, tmp.Length);
+
+ // Write the name
+ stream.WriteByte ((byte)Name.Length);
+ tmp = System.Text.Encoding.ASCII.GetBytes (Name);
+ stream.Write (tmp, 0, tmp.Length);
+
+ // Pad the name
+ if (tmp.Length % 2 == 0)
+ stream.WriteByte (0);
+
+ // Write the data
+ tmp = FSpot.BitConverter.GetBytes ((uint)Data.Length, false);
+ stream.Write (tmp, 0, tmp.Length);
+
+ stream.Write (Data, 0, Data.Length);
+ // Pad the data
+ if (Data.Length % 2 > 0)
+ stream.WriteByte (0);
+ }
+ }
+
+ public class BimFile : SemWeb.StatementSource
+ {
+ // False seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ System.Collections.ArrayList entries = new System.Collections.ArrayList ();
+
+ public BimFile (System.IO.Stream stream)
+ {
+ Load (stream);
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ foreach (Entry e in entries) {
+ EntryType type = (EntryType) e.Type;
+
+ switch (type) {
+ case EntryType.IPTCNAA:
+ System.IO.Stream iptcstream = new System.IO.MemoryStream (e.Data);
+ FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
+ iptc.Select (sink);
+ break;
+ case EntryType.XMP:
+ System.IO.Stream xmpstream = new System.IO.MemoryStream (e.Data);
+ FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
+ xmp.Select (sink);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public Entry FindEntry (EntryType type)
+ {
+ foreach (Entry current in entries)
+ if (current.Type == (ushort)type)
+ return current;
+
+ return null;
+ }
+
+ public void Load (System.IO.Stream stream)
+ {
+ while (stream.Position < stream.Length)
+ {
+ Entry current = new Entry ();
+ current.Load (stream);
+ //System.Console.WriteLine ("read {0} - {1}", ((EntryType)current.Type).ToString (), current.Name);
+ try {
+ //System.Console.WriteLine (System.Text.Encoding.ASCII.GetString (current.Data));
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ entries.Add (current);
+ }
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ foreach (Entry e in entries) {
+ e.Save (stream);
+ }
+ }
+ }
+}
+
+
+
Added: trunk/beagle/Util/F-Spot/Imaging/Ciff.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/Ciff.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,509 @@
+using System;
+
+namespace FSpot.Ciff {
+ public enum Tag {
+ // Byte valuesad
+ NullRecord = 0x0000,
+ FreeBytes = 0x0001,
+ CanonColorInfo1 = 0x0032,
+
+ // ASCII strings
+ CanonFileDescription = 0x0805,
+ UserComment = 0x0805, // FIXME this is the same as above, is it correct?
+ CanonRawMakeModel = 0x080a,
+ CanonFirmwareVersion = 0x080b,
+ ComponentVersion = 0x08c,
+ ROMOperationMode = 0x08d,
+ OwnerName = 0x0810,
+ CanonImageType = 0x0815,
+ OriginalFileName = 0x0816,
+ ThumbnailFileName = 0x0817,
+
+ // Short values
+ TargetImageType = 0x100a,
+ ShutterReleaseMethod = 0x1010,
+ ShutterReleaseTiming = 0x1011,
+ ReleaseSetting = 0x1016,
+ BaseISO = 0x101c,
+
+ Uknown2 = 0x1028,
+
+ FocalLength = 0x1029,
+ CanonShotInfo = 0x102a,
+ CanonColorInfo2 = 0x102c,
+ CanonCameraSettings = 0x102d,
+ WhiteSample = 0x1030,
+ SensorInfo = 0x1031,
+ CanonCustomFunctions = 0x1033,
+ CanonPictureInfo = 0x1038,
+
+ Unknown3 = 0x1039,
+ Unknown4 = 0x1093,
+ Unknown5 = 0x10a8,
+
+ WhiteBalanceTable = 0x10a9,
+
+ Unknown6 = 0x10aa,
+
+ ColorTemperature = 0x10ae,
+ ColorSapce = 0x10b4,
+
+ Unknown7 = 0x10b5,
+ unknown8 = 0x10c0,
+ Unknown9 = 0x10c1,
+
+ ImageFormat = 0x1803,
+ RecordID = 0x1804,
+ SelfTimerTime = 0x1806,
+ TargetDistanceSetting = 0x1807,
+ SerialNumber = 0x180b,
+ TimeStamp = 0x180e,
+ ImageSpec = 0x1810,
+ FlashInfo = 0x1813,
+ MeasuredEV = 0x1814,
+ FileNumber = 0x1817,
+ ExposureInfo = 0x1818,
+
+ Unknown10 = 0x1834,
+
+ DecoderTable = 0x1835,
+
+ Unknown11 = 0x183b,
+
+ // Image Data
+ RawData = 0x2005,
+ JpgFromRaw = 0x2007,
+ ThumbnailImage = 0x2008,
+
+ // Directories
+ ImageDescrption = 0x2804,
+ CameraObject = 0x2807,
+ ShootingRecord = 0x3002,
+ MeasuredInfo = 0x3003,
+ CameraSpecification = 0x3004,
+ ImageProps = 0x300a,
+ ExifInformation = 0x300b
+ }
+
+
+
+ public struct ImageSpec {
+ public uint ImageWidth; // Number of horizontal pixels
+ public uint ImageHeight; // Number of vertical pixels
+ public float PixelAspectRatio;
+ public int RotationAngle; // degrees counter clockwise to rotate (orientation)
+ public uint ComponentBitDepth; // bits per component
+ public uint ColorBitDepth; // bits per component * channels
+ public uint ColorBW; // byte wise: 0 gray - 1 color ; byte 2 use aspect ratio ; 3 and 4 reserved
+
+ public ImageSpec (byte [] data, bool little)
+ {
+ ImageWidth = BitConverter.ToUInt32 (data, 0, little);
+ ImageHeight = BitConverter.ToUInt32 (data, 4, little);
+
+ PixelAspectRatio = BitConverter.ToSingle (data, 8, little);
+ RotationAngle = BitConverter.ToInt32 (data, 12, little);
+ ComponentBitDepth = BitConverter.ToUInt32 (data, 16, little);
+ ColorBitDepth = BitConverter.ToUInt32 (data, 20, little);
+ ColorBW = BitConverter.ToUInt32 (data, 24, little);
+ System.Console.WriteLine ("0x{0}", ColorBW.ToString ("x"));
+ }
+
+ public PixbufOrientation Orientation {
+ get {
+ int angle = RotationAngle % 360;
+ if (angle < 45)
+ return PixbufOrientation.TopLeft;
+ else if (angle < 135)
+ return PixbufOrientation.RightTop;
+ else if (angle < 225)
+ return PixbufOrientation.BottomRight;
+ else if (angle < 315)
+ return PixbufOrientation.LeftBottom;
+ else
+ return PixbufOrientation.TopLeft;
+ }
+ }
+
+ public bool IsColor {
+ get {
+ return (ColorBW & 1) > 0;
+
+ }
+ }
+
+ public bool HasSquarePixels {
+ get {
+ return (ColorBW & 1 << 1) == 0;
+ }
+ }
+ }
+
+ public struct CaptureTime {
+ uint seconds;
+ int tz_offset;
+ uint tz_data;
+
+ public CaptureTime (byte [] data, bool little)
+ {
+ seconds = BitConverter.ToUInt32 (data, 0, little);
+ tz_offset = BitConverter.ToInt32 (data, 4, little);
+ tz_data = BitConverter.ToUInt32 (data, 8, little);
+ }
+
+ public System.DateTime LocalTime {
+ get {
+ return new System.DateTime (1970, 1, 1).AddSeconds (seconds);
+ }
+ }
+
+ public bool HasTimezone {
+ get {
+ return ((1 << 31 & tz_data) > 0);
+ }
+ }
+
+ public override string ToString ()
+ {
+ string tz = String.Empty;
+
+ if (HasTimezone)
+ if (tz_offset != 0)
+ tz = System.String.Format ("{0}{1}:{2}",
+ seconds > 0 ? "+" : "-",
+ tz_offset / 3600,
+ tz_offset / 60 % 60);
+ else
+ tz = "Z";
+
+
+ return System.String.Format ("{0}{1}", LocalTime.ToString ("yyyy-MM-ddThh:mm:ss"), tz);
+ }
+ }
+
+ public enum EntryType : ushort {
+ Byte = 0x0000,
+ Ascii = 0x0800,
+ Short = 0x1000,
+ Int = 0x1800,
+ Struct = 0x2000,
+ Directory1 = 0x2800,
+ Directory2 = 0x2800,
+ }
+
+ public enum Mask {
+ StorageFormat = 0xc000,
+ Type = 0x3800,
+ ID = 0x07ff,
+ }
+
+ /* See http://www.sno.phy.queensu.ca/~phil/exiftool/canon_raw.html */
+ public struct Entry {
+ internal Tag Tag;
+ internal uint Size;
+ internal uint Offset;
+
+ public Entry (byte [] data, int pos, bool little)
+ {
+ Tag = (Tag) BitConverter.ToUInt16 (data, pos, little);
+ Size = BitConverter.ToUInt32 (data, pos + 2, little);
+ Offset = BitConverter.ToUInt32 (data, pos + 6, little);
+ }
+
+ public EntryType Type {
+ get {
+ return GetType (Tag);
+ }
+ }
+
+ public static EntryType GetType (Tag tag)
+ {
+ EntryType type = (EntryType) ((ushort)tag & (ushort)Mask.Type);
+ return type;
+ }
+
+ public static bool IsDirectory (Tag tag)
+ {
+ EntryType type = GetType (tag);
+ return (type == EntryType.Directory1 || type == EntryType.Directory2);
+ }
+ }
+
+ public class ImageDirectory {
+ System.Collections.ArrayList entry_list;
+ uint Count;
+ bool little;
+ uint start;
+ long DirPosition;
+ System.IO.Stream stream;
+
+ public ImageDirectory (System.IO.Stream stream, uint start, long end, bool little)
+ {
+ this.start = start;
+ this.little = little;
+ this.stream = stream;
+
+ entry_list = new System.Collections.ArrayList ();
+
+ stream.Position = end - 4;
+ byte [] buf = new byte [10];
+ stream.Read (buf, 0, 4);
+ uint directory_pos = BitConverter.ToUInt32 (buf, 0, little);
+ DirPosition = start + directory_pos;
+
+ stream.Position = DirPosition;
+ stream.Read (buf, 0, 2);
+
+ Count = BitConverter.ToUInt16 (buf, 0, little);
+
+ for (int i = 0; i < Count; i++)
+ {
+ stream.Read (buf, 0, 10);
+ System.Console.WriteLine ("reading {0} {1}", i, stream.Position);
+ Entry entry = new Entry (buf, 0, little);
+ entry_list.Add (entry);
+ }
+ }
+
+ public void Dump ()
+ {
+ System.Console.WriteLine ("Dumping directory with {0} entries", entry_list.Count);
+ for (int i = 0; i < entry_list.Count; i++) {
+ Entry e = (Entry) entry_list[i];
+ System.Console.WriteLine ("\tentry[{0}] = {1}.{6}.{5}({4}).{2}-{3}",
+ i, e.Tag, e.Size, e.Offset, e.Tag.ToString ("x"), (uint)e.Tag & ~(uint)Mask.StorageFormat, e.Type);
+ }
+ }
+
+ public ImageDirectory ReadDirectory (Tag tag)
+ {
+ foreach (Entry e in entry_list) {
+ if (e.Tag == tag) {
+ uint subdir_start = this.start + e.Offset;
+ ImageDirectory subdir = new ImageDirectory (stream, subdir_start, subdir_start + e.Size, little);
+ return subdir;
+ }
+ }
+ return null;
+ }
+
+ public byte [] ReadEntry (int pos)
+ {
+ Entry e = (Entry) entry_list [pos];
+
+ stream.Position = this.start + e.Offset;
+
+ byte [] data = new byte [e.Size];
+ stream.Read (data, 0, data.Length);
+
+ return data;
+ }
+
+ public byte [] ReadEntry (Tag tag)
+ {
+ int pos = 0;
+ foreach (Entry e in entry_list) {
+ if (e.Tag == tag)
+ return ReadEntry (pos);
+ pos++;
+ }
+ return null;
+ }
+ }
+
+ public class CiffFile : FSpot.ImageFile , SemWeb.StatementSource {
+ public ImageDirectory root;
+ private uint version;
+ bool little;
+ System.IO.Stream stream;
+
+ // False seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public ImageDirectory Root {
+ get {
+ if (root == null) {
+ stream = Open ();
+ root = Load (stream);
+ }
+
+ return root;
+ }
+ }
+
+ public CiffFile (Uri uri) : base (uri)
+ {
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ byte [] data = null;
+ ImageDirectory props = Root.ReadDirectory (Tag.ImageProps);
+ ImageDirectory camera = props.ReadDirectory (Tag.CameraObject);
+
+ data = props.ReadEntry (Tag.TimeStamp);
+ if (data != null)
+ MetadataStore.AddLiteral (sink, "xmp:CreateDate", new CaptureTime (data, little).ToString ());
+
+ data = props.ReadEntry (Tag.ImageSpec);
+ if (data != null) {
+ ImageSpec spec = new ImageSpec (data, little);
+ MetadataStore.AddLiteral (sink, "tiff:Orientation", ((int)spec.Orientation).ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageWidth", spec.ImageWidth.ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageLength", spec.ImageHeight.ToString ());
+ string comp = spec.ComponentBitDepth.ToString ();
+
+ if (spec.IsColor) {
+ MetadataStore.Add (sink, "tiff:BitsPerSample", "rdf:Seq", new string [] { comp, comp, comp });
+ } else {
+ MetadataStore.Add (sink, "tiff:BitsPerSample", "rdf:Seq", new string [] { comp });
+ }
+
+ if (!spec.HasSquarePixels) {
+ MetadataStore.AddLiteral (sink, "tiff:XResolution",
+ (1000000 * spec.PixelAspectRatio).ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:YResolution",
+ (1000000 * (1 / spec.PixelAspectRatio)).ToString ());
+ }
+
+ }
+
+ data = camera.ReadEntry (Tag.CanonRawMakeModel);
+ if (data != null) {
+ string make_model = System.Text.Encoding.ASCII.GetString (data, 0, data.Length - 1);
+ string [] vals = make_model.Split (new char [] {'\0'});
+ MetadataStore.AddLiteral (sink, "tiff:Make", vals [0]);
+ MetadataStore.AddLiteral (sink, "tiff:Model", vals [1]);
+ }
+
+ /*
+ // FIXME this doesn't appear to be ascii.
+ data = camera.ReadEntry (Tag.OwnerName);
+ if (data != null) {
+ string name = System.Text.Encoding.ASCII.GetString (data, 0, data.Length - 1);
+ MetadataStore.AddLiteral (sink, "dc:creator", "rdf:Seq", new SemWeb.Literal (name));
+ }
+ */
+ }
+
+
+ protected ImageDirectory Load (System.IO.Stream stream)
+ {
+ byte [] header = new byte [26]; // the spec reserves the first 26 bytes as the header block
+ stream.Read (header, 0, header.Length);
+
+ uint start;
+
+ little = (header [0] == 'I' && header [1] == 'I');
+
+ start = BitConverter.ToUInt32 (header, 2, little);
+
+ // HEAP is the type CCDR is the subtype
+ if (System.Text.Encoding.ASCII.GetString (header, 6, 8) != "HEAPCCDR")
+ throw new ImageFormatException ("Invalid Ciff Header Block");
+
+ version = BitConverter.ToUInt32 (header, 14, little);
+
+ //
+
+ long end = stream.Length;
+ return new ImageDirectory (stream, start, end, little);
+ }
+
+ public uint Version {
+ get { return version; }
+ }
+
+ public override PixbufOrientation GetOrientation ()
+ {
+ PixbufOrientation orientation = PixbufOrientation.TopLeft;
+ ImageDirectory props = Root.ReadDirectory (Tag.ImageProps);
+ byte [] data = props.ReadEntry (Tag.ImageSpec);
+
+ if (data != null)
+ orientation = new ImageSpec (data, little).Orientation;
+ else
+ System.Console.WriteLine ("NO ORIENTATION");
+
+ return orientation;
+ }
+
+ public override System.DateTime Date {
+ get {
+ ImageDirectory props = Root.ReadDirectory (Tag.ImageProps);
+ byte [] date = props.ReadEntry (Tag.TimeStamp);
+
+ if (date == null) {
+ System.Console.WriteLine ("NO DATE");
+ return base.Date;
+ }
+
+ return new CaptureTime (date, little).LocalTime.ToUniversalTime ();
+ }
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ byte [] data = GetEmbeddedJpeg ();
+
+ if (data != null)
+ return new System.IO.MemoryStream (data);
+ else
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+#if false
+ public override Gdk.Pixbuf Load (int width, int height)
+ {
+ Gdk.Pixbuf full = this.Load ();
+ Gdk.Pixbuf scaled = PixbufUtils.ScaleToMaxSize (full, width, height);
+ full.Dispose ();
+ return scaled;
+ }
+#endif
+ public void Dump ()
+ {
+ Root.Dump ();
+ ImageDirectory props = Root.ReadDirectory (Tag.ImageProps);
+ props.Dump ();
+ /*
+ string path = "out2.jpg";
+ System.IO.File.Delete (path);
+
+ System.IO.Stream output = System.IO.File.Open (path, System.IO.FileMode.OpenOrCreate);
+ byte [] data = GetEmbeddedThumbnail ();
+ System.Console.WriteLine ("data length {0}", data != null ? data.Length : -1);
+ output.Write (data, 0, data.Length);
+ output.Close ();
+ */
+ }
+
+ public byte [] GetEmbeddedJpeg ()
+ {
+ return Root.ReadEntry (Tag.JpgFromRaw);
+ }
+
+ public byte [] GetEmbeddedThumbnail ()
+ {
+ return Root.ReadEntry (Tag.ThumbnailImage);
+ }
+
+ /*
+ public static void Main (string [] args)
+ {
+ CiffFile ciff = new CiffFile (args [0]);
+ ciff.Dump ();
+ }
+ */
+
+ protected override void Close ()
+ {
+ if (stream != null) {
+ stream.Close ();
+ stream = null;
+ }
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/DCRawFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/DCRawFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,135 @@
+using System.Diagnostics;
+using System.IO;
+using System;
+
+namespace FSpot {
+ public class Pipe : System.IO.Stream {
+ // This class is a hack to make sure mono doesn't dispose the process
+ // and by extension the stream from the pipe when we are still using the
+ // the stream.
+ Process process;
+ Stream stream;
+
+ public override bool CanRead {
+ get { return stream.CanRead; }
+ }
+
+ public override bool CanSeek {
+ get { return stream.CanSeek; }
+ }
+
+ public override bool CanWrite {
+ get { return stream.CanWrite; }
+ }
+
+ public override long Length {
+ get { return stream.Length; }
+ }
+
+ public override long Position {
+ get { return stream.Position; }
+ set { stream.Position = value; }
+ }
+
+ public Pipe (Process p, Stream stream)
+ {
+ this.process = p;
+ this.stream = stream;
+ }
+
+ public override void Flush ()
+ {
+ stream.Flush ();
+ }
+
+ public override int Read (byte [] b, int s, int l)
+ {
+ return stream.Read (b, s, l);
+ }
+
+ public override long Seek (long l, SeekOrigin origin)
+ {
+ return stream.Seek(l, origin);
+ }
+
+ public override void SetLength (long l)
+ {
+ stream.SetLength (l);
+ }
+
+ public override void Write (byte [] b, int s, int l)
+ {
+ stream.Write (b, s, l);
+ }
+
+ public override void Close ()
+ {
+ stream.Close ();
+ stream = null;
+ process.Dispose ();
+ process = null;
+ }
+ }
+
+ public class DCRawFile : ImageFile {
+ const string dcraw_command = "dcraw";
+
+ public DCRawFile (string path) : base (path)
+ {
+ }
+
+ public DCRawFile (Uri uri) : base (uri)
+ {
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ return RawPixbufStream (uri);
+ }
+
+ internal static System.IO.Stream RawPixbufStream (Uri location)
+ {
+#if false
+ string path = location.LocalPath;
+ string [] args = new string [] { dcraw_command, "-h", "-w", "-c", "-t", "0", path };
+
+ InternalProcess proc = new InternalProcess (System.IO.Path.GetDirectoryName (path), args);
+ proc.StandardInput.Close ();
+ return proc.StandardOutput;
+#else
+ return null;
+#endif
+ }
+
+ public static System.IO.Stream RawPixbufStreamOld (string path)
+ {
+ // FIXME this filename quoting is super lame
+ string args = System.String.Format ("-h -w -c -t 0 \"{0}\"", path);
+
+ System.Diagnostics.Process process = new System.Diagnostics.Process ();
+ process.StartInfo = new System.Diagnostics.ProcessStartInfo (dcraw_command, args);
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.UseShellExecute = false;
+ process.Start ();
+ return new Pipe (process, process.StandardOutput.BaseStream);
+ }
+
+#if false
+ public static Gdk.Pixbuf Load (string path, string args)
+ {
+ // FIXME this filename quoting is super lame
+ args = System.String.Format ("-h -w -c \"{0}\"", path);
+
+ System.Console.WriteLine ("path = {0}, args = \"{1}\"", path, args);
+
+ using (System.Diagnostics.Process process = new System.Diagnostics.Process ()) {
+ process.StartInfo = new System.Diagnostics.ProcessStartInfo (dcraw_command, args);
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.UseShellExecute = false;
+ process.Start ();
+ return PixbufUtils.LoadFromStream (process.StandardOutput.BaseStream);
+ }
+ }
+#endif
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/Exif.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/Exif.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,920 @@
+//
+// Exif.cs : LibExif wrapper for FSpot
+//
+// Author:
+// Larry Ewing (lewing novell com)
+// Ravi Pratap (ravi ximian com)
+// Miguel de Icaza (miguel ximian com)
+//
+// (C) 2002, 2004, 2005 Novell, Inc.
+//
+
+using System;
+using System.Collections;
+using System.Runtime.InteropServices;
+
+using Mono.Unix;
+
+namespace Exif {
+ public enum Tag {
+ InteroperabilityIndex = 0x0001,
+ InteroperabilityVersion = 0x0002,
+ ImageWidth = 0x0100,
+ ImageHeight = 0x0101,
+ BitsPersample = 0x0102,
+ Compression = 0x0103,
+ PhotometricInterpretation = 0x0106,
+ FillOrder = 0x010a,
+ DocumentName = 0x010d,
+ ImageDescription = 0x010e,
+ Make = 0x010f,
+ Model = 0x0110,
+ StripOffsets = 0x0111,
+ Orientation = 0x0112,
+ SamplesPerPixel = 0x0115,
+ RowsPerStrip = 0x0116,
+ StripByteCounts = 0x0117,
+ XResolution = 0x011a,
+ YResolution = 0x011b,
+ PlanarConfiguration = 0x011c,
+ ResolutionUnit = 0x0128,
+ TransferFunction = 0x012d,
+ Software = 0x0131,
+ DateTime = 0x0132,
+ Artist = 0x013b,
+ WhitePoint = 0x013e,
+ PrimaryChromaticities = 0x013f,
+ TransferRange = 0x0156,
+ JPEGProc = 0x0200,
+ JPEGInterchangeFormat = 0x0201,
+ JPEGInterchangeFormatLength = 0x0202,
+ YCBCRCoefficients = 0x0211,
+ YCBCRSubSampling = 0x0212,
+ YCBCRPositioning = 0x0213,
+ ReferenceBlackWhite = 0x0214,
+ RelatedImageFileFormat = 0x1000,
+ RelatedImageWidth = 0x1001,
+ RelatedImageHeight = 0x1002,
+ CFARepeatPatternDim = 0x828d,
+ CFAPattern = 0x828e,
+ BatteryLevel = 0x828f,
+ Copyright = 0x8298,
+ ExposureTime = 0x829a,
+ FNumber = 0x829d,
+ IPTCNAA = 0x83bb,
+ ExifIfdPointer = 0x8769,
+ InterColorProfile = 0x8773,
+ ExposureProgram = 0x8822,
+ SpectralSensitivity = 0x8824,
+ GPSInfoIfdPointer = 0x8825,
+ ISOSpeedRatings = 0x8827,
+ OECF = 0x8828,
+ ExifVersion = 0x9000,
+ DateTimeOriginal = 0x9003,
+ DateTimeDigitized = 0x9004,
+ ComponentsConfiguration = 0x9101,
+ CompressedBitsPerPixel = 0x9102,
+ ShutterSpeedValue = 0x9201,
+ ApertureValue = 0x9202,
+ BrightnessValue = 0x9203,
+ ExposureBiasValue = 0x9204,
+ MaxApertureValue = 0x9205,
+ SubjectDistance = 0x9206,
+ MeteringMode = 0x9207,
+ LightSource = 0x9208,
+ Flash = 0x9209,
+ FocalLength = 0x920a,
+ SubjectArea = 0x9214,
+ MakerNote = 0x927c,
+ UserComment = 0x9286,
+ SubSecTime = 0x9290,
+ SubSecTimeOriginal = 0x9291,
+ SubSecTimeDigitized = 0x9292,
+ FlashPixVersion = 0xa000,
+ ColorSpace = 0xa001,
+ PixelXDimension = 0xa002,
+ PixelYDimension = 0xa003,
+ RelatedSoundFile = 0xa004,
+ InteroperabilityIfdPointer = 0xa005,
+ FlashEnergy = 0xa20b,
+ SpatialFrequencyResponse = 0xa20c,
+ FocalPlaneXResolution = 0xa20e,
+ FocalPlaneYResolution = 0xa20f,
+ FocalPlaneResolutionUnit = 0xa210,
+ SubjectLocation = 0xa214,
+ ExposureIndex = 0xa215,
+ SensingMethod = 0xa217,
+ FileSource = 0xa300,
+ SceneType = 0xa301,
+ NewCFAPattern = 0xa302,
+ CustomRendered = 0xa401,
+ ExposureMode = 0xa402,
+ WhiteBalance = 0xa403,
+ DigitalZoomRatio = 0xa404,
+ FocalLengthIn35mmFilm = 0xa405,
+ SceneCaptureType = 0xa406,
+ GainControl = 0xa407,
+ Contrast = 0xa408,
+ Saturation = 0xa409,
+ Sharpness = 0xa40a,
+ DeviceSettingDescription = 0xa40b,
+ SubjectDistanceRange = 0xa40c,
+ ImageUniqueId = 0xa420,
+
+ // The Following IDs are not described the EXIF spec
+
+ // The XMP spec declares that XMP data should live 0x2bc when
+ // embedded in tiff images.
+ XMP = 0x02bc,
+
+ // Print Image Matching data
+ PimIfdPointer = 0xc4a5
+ }
+
+ public enum ByteOrder {
+ Motorola,
+ Intel
+ }
+
+ public enum ExifFormat {
+ Byte = 1,
+ Ascii = 2,
+ Short = 3,
+ Long = 4,
+ Rational = 5,
+ Undefined = 7,
+ Slong = 9,
+ SRational = 10
+ }
+
+ public enum Ifd {
+ Zero = 0,
+ One,
+ Exif,
+ Gps,
+ InterOperability,
+ Count
+ }
+
+ internal class ExifUtil {
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_tag_get_name (Tag tag);
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_tag_get_title (Tag tag);
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_tag_get_description (Tag tag);
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_byte_order_get_name (ByteOrder order);
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_format_get_name (ExifFormat format);
+
+ [DllImport ("libexif.dll")]
+ static extern char exif_format_get_size (ExifFormat format);
+
+ [DllImport ("libexif.dll")]
+ static extern IntPtr exif_ifd_get_name (Ifd ifd);
+
+ public static string GetTagName (Tag tag)
+ {
+
+ IntPtr raw_ret = exif_tag_get_name (tag);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static string GetTagTitle (Tag tag)
+ {
+ IntPtr raw_ret = exif_tag_get_title (tag);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static string GetTagDescription (Tag tag)
+ {
+ IntPtr raw_ret = exif_tag_get_description (tag);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static string GetByteOrderName (ByteOrder order)
+ {
+ IntPtr raw_ret = exif_byte_order_get_name (order);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static string GetFormatName (ExifFormat format)
+ {
+ IntPtr raw_ret = exif_format_get_name (format);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static char GetFormatSize (ExifFormat format)
+ {
+ return exif_format_get_size (format);
+ }
+
+ public static string GetIfdName (Ifd ifd)
+ {
+ IntPtr raw_ret = exif_ifd_get_name (ifd);
+ return Marshal.PtrToStringAnsi (raw_ret);
+ }
+
+ public static string GetIfdNameExtended (Ifd ifd)
+ {
+ switch (ifd) {
+ case Ifd.Zero:
+ return Catalog.GetString ("Image Directory");
+ case Ifd.One:
+ return Catalog.GetString ("Thumbnail Directory");
+ case Ifd.Exif:
+ return Catalog.GetString ("Exif Directory");
+ case Ifd.Gps:
+ return Catalog.GetString ("GPS Directory");
+ case Ifd.InterOperability:
+ return Catalog.GetString ("InterOperability Directory");
+ default:
+ return Catalog.GetString ("Unknown Directory");
+ }
+ }
+
+ public static DateTime DateTimeFromString(string dt)
+ {
+ // Exif DateTime strings are formatted as
+ // "YYYY:MM:DD HH:MM:SS"
+
+ string delimiters = " :";
+ string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
+ DateTime result;
+ result = new DateTime (Int32.Parse(dt_data[0]), Int32.Parse(dt_data[1]), Int32.Parse(dt_data[2]),
+ Int32.Parse(dt_data[3]), Int32.Parse(dt_data[4]), Int32.Parse(dt_data[5]));
+
+ return result;
+ }
+
+ }
+
+ public abstract class ExifObject : IDisposable {
+ protected HandleRef handle;
+
+ public HandleRef Handle {
+ get {
+ return handle;
+ }
+ }
+
+ public ExifObject () {}
+
+ public ExifObject (IntPtr ptr)
+ {
+ handle = new HandleRef (this, ptr);
+ }
+
+ protected abstract void Cleanup ();
+
+ public void Dispose () {
+ Cleanup ();
+ System.GC.SuppressFinalize (this);
+ }
+
+ ~ExifObject ()
+ {
+ Cleanup ();
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct _ExifContent {
+ public IntPtr entries;
+ public uint count;
+ public IntPtr parent;
+
+ public IntPtr priv;
+ }
+
+ public class ExifContent : ExifObject {
+ ExifData parent;
+ public ExifData Parent {
+ get {
+ return parent;
+ }
+ }
+
+ System.Collections.ArrayList entries;
+
+ internal ExifContent (ExifData parent, IntPtr handle) : base (handle)
+ {
+ this.parent = parent;
+ exif_content_ref (this.handle);
+ }
+
+ [DllImport ("libexif.dll")]
+ static extern void exif_content_ref (HandleRef handle);
+
+ [DllImport ("libexif.dll")]
+ static extern void exif_content_unref (HandleRef handle);
+
+ protected override void Cleanup ()
+ {
+ exif_content_unref (handle);
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_content_remove_entry (HandleRef content, HandleRef entry);
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_content_add_entry (HandleRef content, HandleRef entry);
+
+ public ExifEntry Lookup (Tag tag)
+ {
+ Assemble ();
+
+ foreach (ExifEntry entry in entries) {
+ if (entry.Tag == tag) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ public bool Contains (ExifEntry entry)
+ {
+ Assemble ();
+
+ return entries.Contains (entry);
+ }
+
+ public ExifEntry GetEntry (Tag tag)
+ {
+ Assemble ();
+
+ ExifEntry entry = Lookup (tag);
+ if (entry == null)
+ entry = new ExifEntry (this, tag);
+
+ return entry;
+ }
+
+ public void Add (ExifEntry entry)
+ {
+ Assemble ();
+
+ entries.Add (entry);
+ // This call can recurse into this function but it protects
+ // itself by checking if it the content already contains the entry
+ entry.SetParent (this);
+ exif_content_add_entry (this.handle, entry.Handle);
+ }
+
+ public void Remove (ExifEntry entry)
+ {
+ Assemble ();
+
+ entries.Remove (entry);
+ // This call can recurse into this function but it protects
+ // itself by checking if it the content already contains the entry
+ entry.SetParent (null);
+ exif_content_remove_entry (this.handle, entry.Handle);
+ }
+
+ public ExifEntry [] GetEntries ()
+ {
+ Assemble ();
+
+ return (ExifEntry [])entries.ToArray (typeof (ExifEntry));
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static unsafe extern IntPtr exif_content_foreach_entry (HandleRef content,
+ ExifContentForeachEntryFunc func,
+ IntPtr data);
+
+ internal delegate void ExifContentForeachEntryFunc (IntPtr entry_ptr, IntPtr data);
+
+ void AssembleEntry (IntPtr entry, IntPtr data)
+ {
+ entries.Add (new ExifEntry (this, entry));
+ }
+
+ ExifContentForeachEntryFunc func;
+
+ public void Assemble ()
+ {
+ if (entries == null) {
+ entries = new System.Collections.ArrayList ();
+
+ func = new ExifContentForeachEntryFunc (AssembleEntry);
+ exif_content_foreach_entry (this.Handle, func, IntPtr.Zero);
+ }
+ }
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct _ExifEntry {
+ public Tag tag;
+ public int format;
+ public uint components;
+ public IntPtr data;
+ public uint size;
+
+ public IntPtr parent;
+
+ public IntPtr priv;
+ }
+
+
+ public class ExifEntry : ExifObject {
+ ExifContent parent;
+ public ExifContent Parent {
+ get {
+ unsafe {
+ if (_handle->parent != parent.Handle.Handle)
+ throw new Exception ("Invalid Object State");
+
+ return parent;
+ }
+ }
+ }
+ // Don't use this unless you know exactly what you are doing
+ internal void SetParent (ExifContent adoptor) {
+ // NOTE this api is ugly but the check prevent the parent state
+ // from getting confused. See ExifContent Add and Remove for the
+ // other half.
+ if (parent != null && parent.Contains (this))
+ parent.Remove (this);
+
+ if (adoptor != null && !adoptor.Contains (this))
+ adoptor.Add (this);
+
+ parent = adoptor;
+ }
+
+ internal ExifEntry (ExifContent parent, IntPtr native) : base (native)
+ {
+ this.handle = new HandleRef (this, native);
+ this.parent = parent;
+ exif_entry_ref (this.handle);
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_entry_new ();
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_entry_initialize (HandleRef handle, Tag tag);
+
+ public ExifEntry (ExifContent parent, Tag tag)
+ {
+ handle = new HandleRef (this, exif_entry_new ());
+ parent.Add (this);
+ this.Reset (tag);
+ }
+
+ public void Reset (Tag tag)
+ {
+ unsafe {
+ // Free any exsting data so that _initialize will actually set the data
+ if (_handle->data != IntPtr.Zero)
+ ExifData.free (_handle->data);
+ _handle->data = IntPtr.Zero;
+ }
+
+ exif_entry_initialize (handle, tag);
+
+ //FIXME the month string in time fields in libexif ix currently broken so we do our own.
+ if (tag == Tag.DateTime
+ || tag == Tag.DateTimeOriginal
+ || tag == Tag.DateTimeDigitized)
+ this.SetData (System.DateTime.Now);
+
+ }
+
+
+ public void Reset ()
+ {
+ Reset (Tag);
+ }
+
+ protected override void Cleanup ()
+ {
+ exif_entry_unref (this.handle);
+ }
+
+ private unsafe _ExifEntry *_handle {
+ get {
+ return (_ExifEntry *)handle.Handle;
+ }
+ }
+
+ public Tag Tag {
+ get {
+ unsafe {
+ return _handle->tag;
+ }
+ }
+ }
+
+ public ExifFormat Format {
+ get {
+ unsafe {
+ return (ExifFormat) _handle->format;
+ }
+ }
+ }
+
+ public byte [] Data {
+ get {
+ unsafe {
+ byte [] data = new byte [_handle->size];
+
+ if (data.Length > 0)
+ Marshal.Copy (_handle->data, data, 0, (int)_handle->size);
+
+ return data;
+ }
+ }
+ }
+
+ public void SetData (byte [] data, int size)
+ {
+ unsafe {
+ if (data == null || data.Length == 0)
+ throw new System.Exception ("Invalid Length");
+
+ if (_handle->data != IntPtr.Zero)
+ ExifData.free (_handle->data);
+
+ _handle->data = ExifData.malloc ((uint)data.Length);
+ Marshal.Copy (data, 0, _handle->data, data.Length);
+
+ _handle->size = (uint) data.Length;
+ // This needs to be set per type as well but
+ // we do it here as well
+ _handle->components = (uint) (data.Length / size);
+ }
+ }
+
+ public void SetData (byte []data)
+ {
+ SetData (data, 1);
+ }
+
+ public void SetData (uint s)
+ {
+ this.SetData (FSpot.BitConverter.GetBytes (s, this.ByteOrder == ByteOrder.Intel), 4);
+ }
+
+ public void SetData (ushort s)
+ {
+ this.SetData (FSpot.BitConverter.GetBytes (s, this.ByteOrder == ByteOrder.Intel), 2);
+ }
+
+ public void SetData (string value)
+ {
+ int len = System.Text.Encoding.UTF8.GetByteCount (value);
+ byte [] tmp = new byte [len + 1];
+ System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
+ tmp[len] = 0;
+ System.Console.WriteLine ("value = {0} len = {1}", value, len);
+ SetData (tmp, 1);
+ }
+
+ public void SetData (System.DateTime time)
+ {
+ SetData (time.ToString ("yyyy:MM:dd HH:mm:ss"));
+ }
+
+ private unsafe void PutBytes (byte *dest, byte *src, int count)
+ {
+ int i = 0;
+ if (System.BitConverter.IsLittleEndian == (this.ByteOrder == ByteOrder.Intel)) {
+ for (i = 0; i < count; i++) {
+ //System.Console.WriteLine ("Copying normal byte [{0}]= {1}", i, src[i]);
+ dest [i] = src [i];
+ }
+ } else {
+ for (i = 0; i < count; i++) {
+ //System.Console.WriteLine ("Copying swapped byte [{0}]= {1}", i, src[i]);
+ dest [i] = src [count - i -1];
+ }
+ }
+ }
+
+ private unsafe uint ToUInt (byte *src)
+ {
+ uint value;
+ PutBytes ((byte *)&value, (byte *)src, 4);
+ return value;
+ }
+
+ private unsafe ushort ToUShort (byte *src)
+ {
+ ushort value;
+ PutBytes ((byte *)&value, (byte *)src, 2);
+ return value;
+ }
+
+ public uint [] GetDataUInt () {
+ unsafe {
+ uint [] result = new uint [_handle->components];
+ uint *src = (uint *)_handle->data;
+ //System.Console.WriteLine ("copying {0} components", result.Length);
+ for (int i = 0; i < result.Length; i++) {
+ result [i] = ToUInt ((byte *)src);
+ //System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
+ src += i;
+ }
+
+ return result;
+ }
+ }
+
+ public ushort [] GetDataUShort () {
+ unsafe {
+ ushort [] result = new ushort [_handle->components];
+ ushort *src = (ushort *)_handle->data;
+ //System.Console.WriteLine ("copying {0} components", result.Length);
+ for (int i = 0; i < result.Length; i++) {
+ result [i] = ToUShort ((byte *)src);
+ //System.Console.WriteLine ("value[{0}] = {1}", i, result [i]);
+ src += i;
+ }
+
+ return result;
+ }
+ }
+
+
+ public int [] GetDataInt () {
+ return null;
+ }
+
+ public ByteOrder ByteOrder
+ {
+ get {
+ return parent.Parent.GetByteOrder ();
+ }
+ }
+
+ public string Description
+ {
+ get {
+ return ExifUtil.GetTagDescription (Tag);
+ }
+ }
+
+ public string Name
+ {
+ get {
+ return ExifUtil.GetTagName (Tag);
+ }
+ }
+
+ public string Title
+ {
+ get {
+ return ExifUtil.GetTagTitle (Tag);
+ }
+ }
+
+ static int fallback = 0;
+
+ // FIXME this version is only valid in libexif 0.5
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_entry_get_value (HandleRef handle);
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_entry_get_value_brief (HandleRef handle);
+
+ // FIXME this version is only valid in libexif 0.6
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_entry_get_value (HandleRef handle, byte [] value, int maxlen);
+
+ public string Value
+ {
+ get {
+ if (fallback == 0) {
+ try {
+ exif_entry_get_value_brief (this.Handle);
+ fallback = 1;
+ } catch (EntryPointNotFoundException) {
+ fallback = -1;
+ }
+ }
+
+ if (fallback > 0)
+ return Marshal.PtrToStringAnsi (exif_entry_get_value (this.Handle));
+ else {
+ byte [] value = new byte [1024];
+ exif_entry_get_value (this.Handle, value, value.Length);
+
+ int i;
+ for (i = 0; i < value.Length; i++) {
+ if (value [i] == 0)
+ break;
+ }
+ int len = System.Math.Max (i, 0);
+ if (len == 0)
+ return null;
+
+ return System.Text.Encoding.UTF8.GetString (value, 0, len);
+ }
+ }
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_entry_ref (HandleRef handle);
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_entry_unref (HandleRef handle);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct _ExifData {
+ internal IntPtr ifd0;
+ internal IntPtr ifd1;
+ internal IntPtr ifd_exif;
+ internal IntPtr ifd_gps;
+ internal IntPtr ifd_interop;
+
+ internal IntPtr data;
+ internal int size;
+
+ internal IntPtr priv;
+ }
+
+ public class ExifData : ExifObject {
+ System.Collections.ArrayList ifds;
+
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_data_new ();
+
+ public ExifData ()
+ {
+ handle = new HandleRef (this, exif_data_new ());
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_data_new_from_file (string path);
+
+ public ExifData (string filename)
+ {
+ handle = new HandleRef (this, exif_data_new_from_file (filename));
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern IntPtr exif_data_new_from_data (byte [] data, uint size);
+
+ public ExifData (byte [] data)
+ {
+ handle = new HandleRef (this, exif_data_new_from_data (data, (uint) data.Length));
+ }
+
+ public ExifData (byte [] data, uint size)
+ {
+ handle = new HandleRef (this, exif_data_new_from_data (data, size));
+ }
+
+ [DllImport ("libc")]
+ internal static extern void free (IntPtr address);
+
+ [DllImport ("libc")]
+ internal static extern IntPtr malloc (uint size);
+
+ [DllImport ("libexif.dll")]
+ private static extern void exif_data_save_data (HandleRef handle, out IntPtr content, out uint size);
+ public byte [] Save ()
+ {
+ Byte [] content = null;
+ uint size;
+ IntPtr data;
+ unsafe {
+ exif_data_save_data (handle, out data, out size);
+
+ content = new byte [size];
+ Marshal.Copy (data, content, 0, (int)size);
+ free (data);
+ }
+
+ System.Console.WriteLine ("Saved {0} bytes", content.Length);
+ return content;
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_data_unref (HandleRef data);
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_data_free (HandleRef data);
+
+ protected override void Cleanup ()
+ {
+ exif_data_unref (handle);
+ }
+
+ [DllImport ("libexif.dll")]
+ internal static extern void exif_data_dump (HandleRef data);
+
+ public void Dump ()
+ {
+ exif_data_dump (handle);
+ }
+
+ public ExifContent GetContents (Ifd ifd)
+ {
+ Assemble ();
+
+ return (ExifContent) ifds [(int)ifd];
+ }
+
+ public ExifContent [] GetContents ()
+ {
+ Assemble ();
+
+ return (ExifContent []) ifds.ToArray (typeof (ExifContent));
+ }
+
+ [DllImport("libexif.dll")]
+ internal static extern ByteOrder exif_data_get_byte_order (HandleRef handle);
+
+ public ByteOrder GetByteOrder ()
+ {
+ return exif_data_get_byte_order (handle);
+ }
+
+ internal delegate void ExifDataForeachContentFunc (IntPtr content, IntPtr data);
+
+ [DllImport ("libexif.dll")]
+ internal unsafe static extern void exif_data_foreach_content(HandleRef handle, ExifDataForeachContentFunc func, IntPtr data);
+
+ unsafe void AssembleIfds (IntPtr content, IntPtr data)
+ {
+ ifds.Add (new ExifContent (this, content));
+ }
+
+ public ExifEntry LookupFirst (Tag tag)
+ {
+ Assemble ();
+ foreach (ExifContent content in ifds) {
+ if (content == null)
+ continue;
+
+ ExifEntry entry = content.Lookup (tag);
+ if (entry != null)
+ return entry;
+ }
+ return null;
+ }
+
+ public string LookupFirstValue (Tag tag)
+ {
+ ExifEntry entry = LookupFirst (tag);
+ if (entry != null) {
+ return entry.Value;
+ }
+ return null;
+ }
+
+ public void Assemble ()
+ {
+ if (ifds == null) {
+ ifds = new System.Collections.ArrayList ();
+
+ if (handle.Handle != IntPtr.Zero)
+ exif_data_foreach_content (handle, new ExifDataForeachContentFunc (AssembleIfds), IntPtr.Zero);
+ }
+ }
+
+ byte [] empty = new byte [0];
+ public byte [] Data {
+ get {
+ unsafe {
+ _ExifData * obj = (_ExifData *) Handle.Handle;
+ byte [] result;
+
+ if (obj == null || obj->data == (IntPtr) 0)
+ result = empty;
+ else {
+ result = new byte [obj->size];
+ Marshal.Copy (obj->data, result, 0, obj->size);
+
+ }
+ return result;
+ }
+ }
+ set {
+ unsafe {
+ _ExifData * obj = (_ExifData *) Handle.Handle;
+ if (value.Length > 65533)
+ throw new System.Exception ("Thumbnail too large");
+
+ if (obj->data != IntPtr.Zero)
+ free (obj->data);
+
+ obj->data = malloc ((uint)value.Length);
+ Marshal.Copy (value, 0, obj->data, value.Length);
+ }
+ }
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/ImageFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/ImageFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,243 @@
+using System;
+using System.IO;
+using UriUtils = Beagle.Util.UriFu;
+
+namespace FSpot {
+ public class ImageFormatException : ApplicationException {
+ public ImageFormatException (string msg) : base (msg)
+ {
+ }
+ }
+
+ public class ImageFile : IDisposable {
+ protected Uri uri;
+
+ static System.Collections.Hashtable name_table;
+
+ public ImageFile (string path)
+ {
+ this.uri = UriUtils.PathToFileUri (path);
+ }
+
+ public ImageFile (Uri uri)
+ {
+ this.uri = uri;
+ }
+
+ protected Stream Open ()
+ {
+ //Gnome.Vfs.Uri vfs = new Gnome.Vfs.Uri (uri.ToString ());
+ // FIXME, this seems like the sane thing to do, but vfs streams seem to
+ // actually be faster and they need more testing.
+ //if (vfs.IsLocal)
+ return File.OpenRead (uri.LocalPath);
+
+#if false
+ System.Console.WriteLine ("open uri = {0}", uri.ToString ());
+ return new Gnome.Vfs.VfsStream (uri.ToString (), FileMode.Open);
+#endif
+ }
+
+ public virtual Stream PixbufStream ()
+ {
+ return Open ();
+ }
+
+ static ImageFile ()
+ {
+ name_table = new System.Collections.Hashtable ();
+ name_table [".svg"] = typeof (FSpot.Svg.SvgFile);
+ name_table [".gif"] = typeof (ImageFile);
+ name_table [".bmp"] = typeof (ImageFile);
+ name_table [".jpeg"] = typeof (JpegFile);
+ name_table [".jpg"] = typeof (JpegFile);
+ name_table [".png"] = typeof (FSpot.Png.PngFile);
+ name_table [".cr2"] = typeof (FSpot.Tiff.Cr2File);
+ name_table [".nef"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".pef"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".raw"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".kdc"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".arw"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".tiff"] = typeof (FSpot.Tiff.TiffFile);
+ name_table [".tif"] = typeof (FSpot.Tiff.TiffFile);
+ name_table [".orf"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".srf"] = typeof (FSpot.Tiff.NefFile);
+ name_table [".dng"] = typeof (FSpot.Tiff.DngFile);
+ name_table [".crw"] = typeof (FSpot.Ciff.CiffFile);
+ name_table [".ppm"] = typeof (FSpot.Pnm.PnmFile);
+ name_table [".mrw"] = typeof (FSpot.Mrw.MrwFile);
+ name_table [".raf"] = typeof (FSpot.Raf.RafFile);
+ name_table [".x3f"] = typeof (FSpot.X3f.X3fFile);
+ }
+
+ public Uri Uri {
+ get { return this.uri; }
+ }
+
+ public PixbufOrientation Orientation {
+ get { return GetOrientation (); }
+ }
+
+ public virtual string Description
+ {
+ get { return null; }
+ }
+
+#if false
+ public virtual void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+ throw new NotImplementedException ();
+ }
+
+ protected Gdk.Pixbuf TransformAndDispose (Gdk.Pixbuf orig)
+ {
+ if (orig == null)
+ return null;
+
+ Gdk.Pixbuf rotated = PixbufUtils.TransformOrientation (orig, this.Orientation, true);
+ //ValidateThumbnail (photo, rotated);
+ if (rotated != orig)
+ orig.Dispose ();
+
+ return rotated;
+ }
+
+ public virtual Gdk.Pixbuf Load ()
+ {
+ using (Stream stream = PixbufStream ()) {
+ Gdk.Pixbuf orig = new Gdk.Pixbuf (stream);
+ return TransformAndDispose (orig);
+ }
+ }
+
+ public virtual Gdk.Pixbuf Load (int max_width, int max_height)
+ {
+ System.IO.Stream stream = PixbufStream ();
+ if (stream == null) {
+ Gdk.Pixbuf orig = this.Load ();
+ Gdk.Pixbuf scaled = PixbufUtils.ScaleToMaxSize (orig, max_width, max_height);
+ orig.Dispose ();
+ return scaled;
+ }
+
+ using (stream) {
+ PixbufUtils.AspectLoader aspect = new PixbufUtils.AspectLoader (max_width, max_height);
+ return aspect.Load (stream, Orientation);
+ }
+ }
+#endif
+ public virtual PixbufOrientation GetOrientation ()
+ {
+ return PixbufOrientation.TopLeft;
+ }
+#if false
+ // FIXME this need to have an intent just like the loading stuff.
+ public virtual Cms.Profile GetProfile ()
+ {
+ return null;
+ }
+#endif
+ public virtual System.DateTime Date
+ {
+ get {
+ return System.IO.File.GetCreationTimeUtc (uri.LocalPath);
+#if false
+ // FIXME mono uses the file change time (ctime) incorrectly
+ // as the creation time so we try to work around that slightly
+ Gnome.Vfs.FileInfo info = new Gnome.Vfs.FileInfo (uri.ToString ());
+
+ DateTime create = info.Ctime;
+ DateTime write = info.Mtime;
+
+ if (create < write)
+ return create;
+ else
+ return write;
+#endif
+ }
+ }
+
+ [Obsolete ("use HasLoader (System.Uri) instead")]
+ public static bool HasLoader (string path)
+ {
+ return HasLoader (UriUtils.PathToFileUri (path));
+ }
+
+ public static bool HasLoader (Uri uri)
+ {
+ string path = uri.AbsolutePath;
+ string extension = System.IO.Path.GetExtension (path).ToLower ();
+ System.Type t = (System.Type) name_table [extension];
+
+ return (t != null);
+ }
+
+ [Obsolete ("use Create (System.Uri) instead")]
+ public static ImageFile Create (string path)
+ {
+ return Create (UriUtils.PathToFileUri (path));
+ }
+
+ public static ImageFile Create (Uri uri)
+ {
+ string path = uri.AbsolutePath;
+ string extension = System.IO.Path.GetExtension (path).ToLower ();
+ System.Type t = (System.Type) name_table [extension];
+ ImageFile img;
+
+ if (t != null)
+ img = (ImageFile) System.Activator.CreateInstance (t, new object[] { uri });
+ else
+ img = new ImageFile (uri);
+
+ return img;
+ }
+
+ // FIXME these are horrible hacks to get a temporary name
+ // with the right extension for ImageFile until we use the mime data
+ // properly. It is here to make sure we can find the places that use
+ // this hack
+ public static string TempPath (string name)
+ {
+ return TempPath (name, System.IO.Path.GetExtension (name));
+ }
+
+ public static string TempPath (string name, string extension)
+ {
+ string temp = System.IO.Path.GetTempFileName ();
+ string imgtemp = temp + "." + extension;
+
+ System.IO.File.Move (temp, imgtemp);
+
+ return imgtemp;
+ }
+
+ public void Dispose ()
+ {
+ Close ();
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Close ()
+ {
+ }
+
+ public static bool IsRaw (string name)
+ {
+ string [] raw_extensions = {".nef", ".crw", ".cr2", ".arw", ".mrw", ".pef", ".dng"};
+ foreach (string ext in raw_extensions)
+ if (ext == System.IO.Path.GetExtension (name).ToLower ())
+ return true;
+ return false;
+ }
+
+ public static bool IsJpeg (string name)
+ {
+ string [] jpg_extensions = {".jpg", ".jpeg"};
+ foreach (string ext in jpg_extensions)
+ if (ext == System.IO.Path.GetExtension (name).ToLower ())
+ return true;
+ return false;
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/IptcFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/IptcFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,427 @@
+using SemWeb;
+using Mono.Unix;
+
+namespace FSpot.Iptc {
+ public enum Format
+ {
+ Unknown,
+ String,
+ Numeric,
+ Binary,
+ Byte,
+ Short,
+ Int,
+ Date,
+ Time
+ };
+
+ public enum Record
+ {
+ Envelope = 1 << 8,
+ Application = 2 << 8,
+ NewsphotoParameter = 3 << 8,
+ NotAllocated1 = 4 << 8,
+ NotAllocated2 = 5 << 8,
+ AbstractRelationship = 6 << 8,
+ PreObjectData = 7 << 8,
+ ObjectData = 8 << 8,
+ PostObjectData = 9 << 8
+ }
+
+ public enum DataSetID
+ {
+ ModelVersion = Record.Envelope | 0,
+ Destination = Record.Envelope | 5,
+ FileFormat = Record.Envelope | 20,
+ FileFormatVersion = Record.Envelope | 22,
+ ServiceIdentifier = Record.Envelope | 30,
+ EnvelopeNumber = Record.Envelope | 40,
+ ProductID = Record.Envelope | 50,
+ EnvelopePriority = Record.Envelope | 60,
+ DateSent = Record.Envelope | 70,
+ TimeSent = Record.Envelope | 80,
+ CodedCharacterSet = Record.Envelope | 90,
+ UNO = Record.Envelope | 100,
+ ARMIdentifier = Record.Envelope | 120,
+ ARMVersion = Record.Envelope | 122,
+
+ RecordVersion = Record.Application | 0,
+ ObjectTypeReference = Record.Application | 3,
+ ObjectAttributeReference = Record.Application | 4,
+ ObjectName = Record.Application | 5,
+ EditStatus = Record.Application | 7,
+ EditorialUpdate = Record.Application | 8,
+ Urgency = Record.Application | 10,
+ SubjectReference = Record.Application | 12,
+ Category = Record.Application | 15,
+ SupplementalCategory = Record.Application | 20,
+ FixtureIdentifier = Record.Application | 22,
+ Keywords = Record.Application | 25,
+ ContentLocationCode = Record.Application | 26,
+ ContentLocationName = Record.Application | 27,
+ ReleaseDate = Record.Application | 30,
+ ReleaseTime = Record.Application | 35,
+ ExpirationDate = Record.Application | 37,
+ ExpirationTime = Record.Application | 38,
+ SpecialInstructions = Record.Application | 40,
+ ActionAdvised = Record.Application | 42,
+ ReferenceService = Record.Application | 45,
+ ReferenceDate = Record.Application | 47,
+ ReferenceNumber = Record.Application | 50,
+ DateCreated = Record.Application | 55,
+ TimeCreated = Record.Application | 60,
+ DigitalCreationDate = Record.Application | 62,
+ DigitalCreationTime = Record.Application | 63,
+ OriginatingProgram = Record.Application | 65,
+ ProgramVersion = Record.Application | 70,
+ ObjectCycle = Record.Application | 75,
+ ByLine = Record.Application | 80,
+ ByLineTitle = Record.Application | 85,
+ City = Record.Application | 90,
+ Sublocation = Record.Application | 92,
+ ProvinceState = Record.Application | 95,
+ PrimaryLocationCode = Record.Application | 100,
+ PrimaryLocationName = Record.Application | 101,
+ OriginalTransmissionReference = Record.Application | 103,
+ Headline = Record.Application | 105,
+ Credit = Record.Application | 110,
+ Source = Record.Application | 115,
+ CopyrightNotice = Record.Application | 116,
+ Contact = Record.Application | 118,
+ CaptionAbstract = Record.Application | 120,
+ WriterEditor = Record.Application | 122,
+ RasterizedCaption = Record.Application | 125,
+ ImageType = Record.Application | 130,
+ ImageOrientation = Record.Application | 131,
+ LanguageIdentifier = Record.Application | 135,
+ AudioType = Record.Application | 150,
+ AudioSamplingRate = Record.Application | 151,
+ AudioSamplingReduction = Record.Application | 152,
+ AudioDuration = Record.Application | 153,
+ AudioOutcue = Record.Application | 154,
+ ObjectDataPreviewFileFormat = Record.Application | 200,
+ ObjectDataPreviewFileFormatVersion = Record.Application | 201,
+ ObjectDataPreviewData = Record.Application | 202,
+
+ SizeMode = Record.PreObjectData | 10,
+ MaxSubfileSize = Record.PreObjectData | 20,
+ ObjectDataSizeAnnounced = Record.PreObjectData | 90,
+ MaximumObjectDataSize = Record.PreObjectData | 95,
+
+ Subfile = Record.ObjectData | 10,
+
+ ConfirmedObjectDataSize = Record.PostObjectData | 10
+ }
+
+ public class DataSetInfo
+ {
+ DataSetID ID;
+ public string Name;
+ public string Description;
+ public string XmpName;
+ bool Mandatory;
+ bool Repeatable;
+ uint MinSize;
+ uint MaxSize;
+ public Format Format;
+
+ public static System.Collections.Hashtable IDTable;
+
+ static DataSetInfo ()
+ {
+ IDTable = new System.Collections.Hashtable ();
+ foreach (DataSetInfo info in datasets) {
+ IDTable [info.ID] = info;
+ }
+ }
+
+ public override string ToString ()
+ {
+ return System.String.Format ("{0}-({1},{2},{3},{4})", Name, Mandatory, Repeatable, MinSize, MaxSize);
+ }
+
+ private static DataSetInfo [] datasets = {
+ new DataSetInfo (DataSetID.ModelVersion, Format.Short, "Model Version", true, false, 2, 2,
+ Catalog.GetString ("IPTC Information Interchange Model (IIM) Version number")),
+ new DataSetInfo (DataSetID.Destination, Format.String, "Destination", false, true, 0, 1024,
+ Catalog.GetString ("OSI Destination routing information")),
+ new DataSetInfo (DataSetID.FileFormat, Format.Short, "File Format", true, false, 2, 2,
+ Catalog.GetString ("IPTC file format")),
+ new DataSetInfo (DataSetID.ServiceIdentifier, Format.String, "Service Identifier", true, false, 0, 10,
+ Catalog.GetString ("Identifies the provider and product")),
+ new DataSetInfo (DataSetID.EnvelopeNumber, Format.Numeric, "Envelope Number", true, false, 8, 8,
+ Catalog.GetString ("A unique number identifying the envelope")), // FIXME
+ new DataSetInfo (DataSetID.ProductID, Format.Numeric, "Product I.D.", false, true, 0, 32,
+ Catalog.GetString ("A unique number")), // FIXME
+ new DataSetInfo (DataSetID.EnvelopePriority, Format.Numeric, "Envelope Priority", false, false, 1, 1,
+ Catalog.GetString ("The envelope handling priority between 1 (most urgent) and 9 (least urgent)")),
+ new DataSetInfo (DataSetID.DateSent, Format.Date, "Date Sent", true, false, 8, 8,
+ Catalog.GetString ("The year, month and day (CCYYMMDD) the service sent the material")),
+ new DataSetInfo (DataSetID.TimeSent, Format.Date, "Time Sent", false, false, 11, 11,
+ Catalog.GetString ("The hour, minute and second (HHMMSS) the service sent the material")),
+ new DataSetInfo (DataSetID.CodedCharacterSet, Format.Time, "Coded Character Set", false, false, 0, 32,
+ Catalog.GetString ("The character set designation")), // FIXME
+ new DataSetInfo (DataSetID.UNO, Format.String, "Unique Name of Object", false, false, 14, 80,
+ Catalog.GetString ("External globally unique object identifier")),
+ // UCD : IPR : ODE : OVI
+ // 8 :[1-32]:[61 - IPR count]:[1-9]
+
+ new DataSetInfo (DataSetID.ARMIdentifier, Format.Short, "ARM Identifier", false, false, 2, 2,
+ Catalog.GetString ("Abstract Relationship Method (ARM) identifier")),
+ new DataSetInfo (DataSetID.ARMVersion, Format.Short, "ARM Version", false, false, 2, 2,
+ Catalog.GetString ("Abstract Relationship Method (ARM) version number.")),
+
+ new DataSetInfo (DataSetID.RecordVersion, Format.Short, "Record Version", false, false, 2, 2,
+ Catalog.GetString ("Number identifying the IIM version this application record uses")),
+ new DataSetInfo (DataSetID.ObjectTypeReference, Format.String, "Object Type Reference", false, false, 3, 64,
+ Catalog.GetString ("Object type reference")), // FIXME
+ // Object Type Number : Object Type Name
+ // 2 : [0-64]
+
+ new DataSetInfo (DataSetID.ObjectAttributeReference, Format.String, "Object Attribute Reference", false, true, 4, 68,
+ Catalog.GetString ("Object attribute reference")), // FIXME
+
+ // Object Attribute number : Object Attribute Name
+ // 3 : [0-64]
+
+ new DataSetInfo (DataSetID.ObjectName, Format.String, "Object Name", false, false, 4, 68,
+ Catalog.GetString ("Object name"), "dc:title"), // FIXME
+ new DataSetInfo (DataSetID.EditStatus, Format.String, "Edit Status", false, false, 0, 64,
+ Catalog.GetString ("Status of the objectdata according to the provider")),
+ new DataSetInfo (DataSetID.EditorialUpdate, Format.String, "Object Name", false, false, 4, 68,
+ Catalog.GetString ("Object name")), // FIXME
+ new DataSetInfo (DataSetID.Sublocation, Format.String, "Location", false, false, 0, 32,
+ Catalog.GetString ("Location within a city or area where the object originates"),
+ "Iptc4xmpCore:Location"),
+ new DataSetInfo (DataSetID.City, Format.String, "City", false, false, 0, 32,
+ Catalog.GetString ("Name of the city the content is focussing on"),
+ "photoshop:City"),
+ new DataSetInfo (DataSetID.CopyrightNotice, Format.String, "Copyright Notice", false, false, 0, 128,
+ Catalog.GetString ("Copyright information for"),
+ "dc:rights"),
+ new DataSetInfo (DataSetID.PrimaryLocationName, Format.String, "Country", false, false, 0, 64,
+ Catalog.GetString ("Full name of the country of the focus of the content"),
+ "photoshop:Country"),
+ new DataSetInfo (DataSetID.PrimaryLocationCode, Format.String, "ISO Country Code", false, false, 0, 3,
+ Catalog.GetString ("Two or three letter ISO3166 code of the country of the focus of the content"),
+ "Iptc4xmpCore:CountryCode"),
+ new DataSetInfo (DataSetID.ByLine, Format.String, "Creator", false, false, 0, 32,
+ Catalog.GetString ("Creator of the content"), // FIXME
+ "dc:creator"),
+ new DataSetInfo (DataSetID.Credit, Format.String, "Provider", false, false, 0, 32,
+ Catalog.GetString ("Provider of the object"),
+ "photoshop:Credit"),
+ new DataSetInfo (DataSetID.ByLineTitle, Format.String, "Creator's Jobtitle", false, true, 0, 32,
+ Catalog.GetString ("The title of the author or creator"),
+ "photoshop:AuthorsPosition"),
+ new DataSetInfo (DataSetID.WriterEditor, Format.String, "Caption/Description writer", false, true, 0, 32,
+ Catalog.GetString ("The person involved in writing, editing or " +
+ "correcting the object data or caption/abstract"),
+ "photoshop:CaptionWriter"),
+ new DataSetInfo (DataSetID.Headline, Format.String, "Headline", false, false, 0, 256,
+ Catalog.GetString ("Headline of the content"),
+ "photoshop:Headline"),
+ new DataSetInfo (DataSetID.SpecialInstructions, Format.String, "Instructions", false, false, 0, 256,
+ Catalog.GetString ("Instructions from the creator to the receiver not covered by other fields"),
+ "photoshop:Instructions"),
+ new DataSetInfo (DataSetID.ObjectAttributeReference, Format.String, "Intellectual genre", false, true, 4, 68,
+ Catalog.GetString ("Intellectual genre of the object"),
+ "Iptc4xmpCore:IntellectualGenre"),
+ // Object Attribute number : Object Attribute Name
+ // 3 : [0-64]
+ };
+
+ public static DataSetInfo FindInfo (DataSetID id)
+ {
+ foreach (DataSetInfo info in datasets)
+ if (id == (DataSetID)info.ID)
+ return info;
+
+ return new DataSetInfo (id, Format.Unknown, "Unknown", false, false, 3, 64,
+ Catalog.GetString ("Unknown IIM DataSet"));
+ }
+
+ protected DataSetInfo (DataSetID id, Format format, string name, bool mandatory, bool repeatable, uint min, uint max, string description) : this (id, format, name, mandatory, repeatable, min, max, description, null)
+ { }
+
+ protected DataSetInfo (DataSetID id, Format format, string name, bool mandatory, bool repeatable, uint min, uint max, string description, string xmpname)
+ {
+ ID = id;
+ Name = name;
+ Description = description;
+ Format = format;
+ Mandatory = mandatory;
+ Repeatable = repeatable;
+ MinSize = min;
+ MaxSize = max;
+ XmpName = xmpname;
+ }
+ }
+
+ public class DataSet
+ {
+ public byte RecordNumber;
+ public byte DataSetNumber;
+ public byte [] Data;
+
+ const byte TagMarker = 0x1c;
+ const ushort LengthMask = 1 << 15;
+
+ public void Load (System.IO.Stream stream)
+ {
+ byte [] rec = new byte [5];
+ int len = stream.Read (rec, 0, rec.Length);
+ if (rec [0] != TagMarker) {
+ throw new System.Exception (System.String.Format ("Invalid tag marker found {0} != {1} with {2} bytes remaining {3}",
+ rec[0].ToString ("x"), TagMarker.ToString ("x"), stream.Length - stream.Position, len));
+ }
+ RecordNumber = rec [1];
+ DataSetNumber = rec [2];
+
+ ulong length = FSpot.BitConverter.ToUInt16 (rec, 3, false);
+
+ if ((length & (LengthMask)) > 0) {
+ // Note: if the high bit of the length is set the record is more than 32k long
+ // and the length is stored in what would normaly be the record data, so we read
+ // that data convert it to a long and continue on.
+ ushort lsize = (ushort)((ushort)length & ~LengthMask);
+ if (lsize > 8)
+ throw new System.Exception ("Wow, that is a lot of data");
+
+ byte [] ldata = new byte [8];
+ stream.Read (ldata, 8 - lsize, lsize);
+ length = FSpot.BitConverter.ToUInt64 (ldata, 0, false);
+ }
+
+ // FIXME if the length is greater than 32768 we re
+ Data = new byte [length];
+ stream.Read (Data, 0, Data.Length);
+ }
+
+ public DataSetID ID {
+ get {
+ return (DataSetID) (RecordNumber << 8 | DataSetNumber);
+ }
+ }
+
+ public string XmpPredicate {
+ get {
+ DataSetInfo info = (DataSetInfo) DataSetInfo.IDTable [this.ID];
+ if (info != null && info.XmpName != null) {
+ return MetadataStore.Namespaces.Resolve (info.XmpName);
+ }
+ return null;
+ }
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ stream.WriteByte (TagMarker);
+ stream.WriteByte (RecordNumber);
+ stream.WriteByte (DataSetNumber);
+ if (Data.Length < LengthMask) {
+ byte [] len = FSpot.BitConverter.GetBytes ((ushort)Data.Length, false);
+ stream.Write (len, 0, len.Length);
+ } else {
+ byte [] len = FSpot.BitConverter.GetBytes ((ushort)LengthMask & 8, false);
+ stream.Write (len, 0, len.Length);
+ len = FSpot.BitConverter.GetBytes ((ulong) Data.Length, false);
+ stream.Write (len, 0, len.Length);
+ }
+ stream.Write (Data, 0, Data.Length);
+ }
+
+ public string XmpObject
+ {
+ get {
+ //DataSetInfo info = (DataSetInfo) DataSetInfo.IDTable [this.ID];
+ //if (info != null && info.Format == Format.String) {
+ return System.Text.Encoding.UTF8.GetString (this.Data);
+ //}
+ //return null;
+ }
+ }
+ }
+
+ public class IptcFile : SemWeb.StatementSource
+ {
+ System.Collections.ArrayList sets = new System.Collections.ArrayList ();
+
+ // False seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public IptcFile (System.IO.Stream stream)
+ {
+ Load (stream);
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ Entity keywords = null;
+
+ foreach (DataSet data in sets) {
+ switch (data.ID) {
+ case DataSetID.CopyrightNotice:
+ MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt", new SemWeb.Literal (data.XmpObject, "x-default", null));
+ break;
+ case DataSetID.ByLine:
+ MetadataStore.AddLiteral (sink, "dc:creator", "rdf:Seq", new SemWeb.Literal (data.XmpObject, "x-default", null));
+ break;
+ case DataSetID.CaptionAbstract:
+ MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt", new SemWeb.Literal (data.XmpObject, "x-default", null));
+ break;
+ case DataSetID.ObjectName:
+ MetadataStore.AddLiteral (sink, "dc:title", "rdf:Alt", new SemWeb.Literal (data.XmpObject, "x-default", null));
+ break;
+ case DataSetID.Keywords:
+ if (keywords == null) {
+ keywords = new BNode ();
+ sink.Add (new Statement (MetadataStore.FSpotXMPBase,
+ MetadataStore.Namespaces.Resolve ("dc:subject"),
+ keywords));
+ sink.Add (new Statement (keywords,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:type"),
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:Bag")));
+ }
+ sink.Add (new Statement (keywords,
+ MetadataStore.Namespaces.Resolve ("rdf:li"),
+ new SemWeb.Literal (data.XmpObject, "x-default", null)));
+ break;
+ default:
+ if (data.XmpPredicate != null) {
+ sink.Add (new Statement (MetadataStore.FSpotXMPBase,
+ (Entity)data.XmpPredicate,
+ new SemWeb.Literal (data.XmpObject)));
+ }
+ break;
+ }
+ }
+ }
+
+ public void Load (System.IO.Stream stream)
+ {
+ while (stream.Position < stream.Length) {
+ DataSet data = new DataSet ();
+
+ try {
+ data.Load (stream);
+ } catch (System.Exception) {
+ //System.Console.WriteLine (e.ToString ());
+ }
+ //DataSetInfo info = DataSetInfo.FindInfo (data.ID);
+ //System.Console.WriteLine ("{0}:{1} - {2} {3}", data.RecordNumber, data.DataSetNumber,
+ // data.ID.ToString (), info.Description);
+ sets.Add (data);
+ }
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ foreach (DataSet data in sets) {
+ data.Save (stream);
+ }
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/JpegFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/JpegFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,401 @@
+#define USE_TIFF
+using System;
+using System.IO;
+using FSpot.Xmp;
+using FSpot.Tiff;
+
+#if ENABLE_NUNIT
+using NUnit.Framework;
+#endif
+
+namespace FSpot {
+ public interface IThumbnailContainer {
+#if false
+ Gdk.Pixbuf GetEmbeddedThumbnail ();
+#endif
+ }
+
+ public class JpegFile : ImageFile, IThumbnailContainer, SemWeb.StatementSource {
+ private Exif.ExifData exif_data;
+ private XmpFile xmp;
+ private JpegHeader header;
+ private FSpot.Tiff.Header exif_header;
+
+ // False seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public JpegFile (Uri uri) : base (uri)
+ {
+ try {
+ // Console.WriteLine ("approximate quality = {0}", Header.GuessQuality ());
+ } catch (Exception e) {
+ System.Console.WriteLine (e);
+ }
+ }
+
+ public JpegFile (string path) : base (path)
+ {
+ }
+
+ public JpegHeader Header {
+ get {
+ if (header == null) {
+ using (Stream stream = this.Open ()) {
+ header = new JpegHeader (stream, true);
+ }
+ }
+ return header;
+ }
+ }
+
+ public FSpot.Tiff.Header ExifHeader {
+ get {
+ if (exif_header == null) {
+ exif_header = Header.GetExifHeader ();
+ }
+ return exif_header;
+ }
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ Header.Select (sink);
+ }
+
+#if false
+ public override Cms.Profile GetProfile ()
+ {
+ return Header.GetProfile ();
+ }
+#endif
+ public override string Description {
+ get {
+#if USE_TIFF
+ try {
+ SubdirectoryEntry sub = (SubdirectoryEntry) ExifHeader.Directory.Lookup (TagId.ExifIfdPointer);
+ if (sub != null) {
+ DirectoryEntry entry = sub.Directory [0].Lookup (TagId.UserComment);
+ if (entry != null)
+ return entry.ValueAsString [0];
+ }
+ } catch (System.Exception e) {
+ Console.WriteLine (e);
+ }
+ return null;
+#else
+ try {
+ Exif.ExifContent exif_content = this.ExifData.GetContents (Exif.Ifd.Exif);
+ Exif.ExifEntry entry = exif_content.Lookup (Exif.Tag.UserComment);
+
+ if (entry == null)
+ return null;
+
+ UserComment comment = new UserComment (entry.Data, entry.ByteOrder == Exif.ByteOrder.Intel);
+ return comment.Value;
+ } catch (Exception e) {
+ // errors here shouldn't be fatal
+ return null;
+ }
+#endif
+ }
+ }
+
+ public void SetDescription (string value)
+ {
+ Exif.ExifContent exif_content = this.ExifData.GetContents (Exif.Ifd.Exif);
+ Exif.ExifEntry entry = exif_content.GetEntry (Exif.Tag.UserComment);
+
+ UserComment comment = new UserComment (value);
+ byte [] data = comment.GetBytes (entry.ByteOrder == Exif.ByteOrder.Intel);
+ entry.SetData (data);
+ }
+
+ public void SetXmp (XmpFile xmp)
+ {
+ this.xmp = xmp;
+ }
+
+ private void UpdateMeta ()
+ {
+ Exif.ExifContent image_content = this.ExifData.GetContents (Exif.Ifd.Zero);
+ image_content.GetEntry (Exif.Tag.Software).SetData ("F-Spot" + " version " + "r3823");
+
+ // set the write time in the datetime tag
+ image_content.GetEntry (Exif.Tag.DateTime).Reset ();
+ }
+
+ private void SaveMetaData (System.IO.Stream input, System.IO.Stream output)
+ {
+ JpegHeader header = new JpegHeader (input);
+ UpdateMeta ();
+
+ // Console.WriteLine ("updated metadata");
+ header.SetExif (this.ExifData);
+ // Console.WriteLine ("set exif");
+ if (xmp != null)
+ header.SetXmp (xmp);
+ // Console.WriteLine ("set xmp");
+ header.Save (output);
+ // Console.WriteLine ("saved");
+ }
+
+#if false
+ public void SaveMetaData (string path)
+ {
+ UpdateMeta ();
+
+ //Get file permissions... (mkstemp does not keep permissions or ownership)
+ Mono.Unix.Native.Stat stat;
+ int stat_err = Mono.Unix.Native.Syscall.stat (path, out stat);
+
+ string temp_path = path;
+ using (System.IO.FileStream stream = System.IO.File.OpenRead (path)) {
+ using (System.IO.Stream output = FSpot.Utils.Unix.MakeSafeTemp (ref temp_path)) {
+ SaveMetaData (stream, output);
+ }
+ }
+
+ File.SetAttributes (temp_path, File.GetAttributes (path));
+
+ if (FSpot.Utils.Unix.Rename (temp_path, path) < 0) {
+ System.IO.File.Delete (temp_path);
+ throw new System.Exception (System.String.Format ("Unable to rename {0} to {1}",
+ temp_path, path));
+ }
+
+ //Set file permissions and gid...
+ if (stat_err == 0)
+ try {
+ Mono.Unix.Native.Syscall.chmod (path, stat.st_mode |
+ Mono.Unix.Native.FilePermissions.S_IRUSR |
+ Mono.Unix.Native.FilePermissions.S_IWUSR);
+ Mono.Unix.Native.Syscall.chown(path, Mono.Unix.Native.Syscall.getuid (), stat.st_gid);
+ } catch (Exception) {}
+ }
+
+ public void SetThumbnail (Gdk.Pixbuf source)
+ {
+ // Then create the thumbnail
+ // The DCF spec says thumbnails should be 160x120 always
+ Gdk.Pixbuf thumbnail = PixbufUtils.ScaleToAspect (source, 160, 120);
+ byte [] thumb_data = PixbufUtils.Save (thumbnail, "jpeg", null, null);
+
+ // System.Console.WriteLine ("saving thumbnail");
+
+ // now update the exif data
+ ExifData.Data = thumb_data;
+ }
+#endif
+
+ public void SetDimensions (int width, int height)
+ {
+ Exif.ExifEntry e;
+ Exif.ExifContent thumb_content;
+
+ // update the thumbnail related image fields if they exist.
+ thumb_content = this.ExifData.GetContents (Exif.Ifd.One);
+ e = thumb_content.Lookup (Exif.Tag.RelatedImageWidth);
+ if (e != null)
+ e.SetData ((uint)width);
+
+ e = thumb_content.Lookup (Exif.Tag.RelatedImageHeight);
+ if (e != null)
+ e.SetData ((uint)height);
+
+ Exif.ExifContent image_content;
+ image_content = this.ExifData.GetContents (Exif.Ifd.Zero);
+ image_content.GetEntry (Exif.Tag.Orientation).SetData ((ushort)PixbufOrientation.TopLeft);
+ //image_content.GetEntry (Exif.Tag.ImageWidth).SetData ((uint)pixbuf.Width);
+ //image_content.GetEntry (Exif.Tag.ImageHeight).SetData ((uint)pixbuf.Height);
+ image_content.GetEntry (Exif.Tag.PixelXDimension).SetData ((uint)width);
+ image_content.GetEntry (Exif.Tag.PixelYDimension).SetData ((uint)height);
+ }
+
+#if false
+ public override void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+
+ // Console.WriteLine ("starting save");
+ // First save the imagedata
+ int quality = Header.GuessQuality ();
+ quality = quality == 0 ? 75 : quality;
+ byte [] image_data = PixbufUtils.Save (pixbuf, "jpeg", new string [] {"quality" }, new string [] { quality.ToString () });
+ System.IO.MemoryStream buffer = new System.IO.MemoryStream ();
+ buffer.Write (image_data, 0, image_data.Length);
+ buffer.Position = 0;
+
+ // Console.WriteLine ("setting thumbnail");
+ SetThumbnail (pixbuf);
+ SetDimensions (pixbuf.Width, pixbuf.Height);
+ pixbuf.Dispose ();
+
+ // Console.WriteLine ("saving metatdata");
+ SaveMetaData (buffer, stream);
+ // Console.WriteLine ("done");
+ buffer.Close ();
+ }
+
+ public Gdk.Pixbuf GetEmbeddedThumbnail ()
+ {
+ if (this.ExifData.Data.Length > 0) {
+ MemoryStream mem = new MemoryStream (this.ExifData.Data);
+ Gdk.Pixbuf thumb = new Gdk.Pixbuf (mem);
+ Gdk.Pixbuf rotated = PixbufUtils.TransformOrientation (thumb, this.Orientation);
+
+ if (rotated != thumb)
+ thumb.Dispose ();
+
+ mem.Close ();
+ return rotated;
+ }
+ return null;
+ }
+#endif
+ public Exif.ExifData ExifData {
+ get {
+ if (exif_data == null) {
+ exif_data = Header.Exif;
+
+ if (exif_data == null || exif_data.Handle.Handle == System.IntPtr.Zero)
+ exif_data = new Exif.ExifData ();
+ }
+ // System.Console.WriteLine ("loading exif data");
+ return exif_data;
+ }
+ set {
+ this.exif_data = value;
+ }
+ }
+
+ public override PixbufOrientation GetOrientation ()
+ {
+ PixbufOrientation orientation = PixbufOrientation.TopLeft;
+#if USE_TIFF
+ try {
+ DirectoryEntry e = ExifHeader.Directory.Lookup (TagId.Orientation);
+ orientation = (PixbufOrientation)e.ValueAsLong [0];
+ } catch {
+ System.Console.WriteLine ("error checking orientation");
+ }
+#else
+ Exif.ExifEntry e = this.ExifData.GetContents (Exif.Ifd.Zero).Lookup (Exif.Tag.Orientation);
+
+ if (e != null) {
+ ushort [] value = e.GetDataUShort ();
+ orientation = (PixbufOrientation) value [0];
+ }
+#endif
+ if (orientation < PixbufOrientation.TopLeft || orientation > PixbufOrientation.LeftBottom)
+ orientation = PixbufOrientation.TopLeft;
+
+ return orientation;
+ }
+
+ public void SetOrientation (PixbufOrientation orientation)
+ {
+ Exif.ExifEntry e = this.ExifData.GetContents (Exif.Ifd.Zero).GetEntry (Exif.Tag.Orientation);
+ // System.Console.WriteLine ("Saving orientation as {0}", orientation);
+ e.SetData ((ushort)orientation);
+
+ e = this.ExifData.GetContents (Exif.Ifd.One).Lookup (Exif.Tag.Orientation);
+ if (e != null)
+ e.SetData ((ushort)orientation);
+ }
+
+ public void SetDateTimeOriginal (DateTime time)
+ {
+ Exif.ExifEntry e = ExifData.LookupFirst (Exif.Tag.DateTimeOriginal);
+ if (e != null)
+ e.SetData (time);
+ else {
+ Exif.ExifContent exif_content = this.ExifData.GetContents (Exif.Ifd.Exif);
+ Exif.ExifEntry entry = exif_content.GetEntry (Exif.Tag.DateTimeOriginal);
+ entry.SetData (time);
+ }
+ }
+
+ public override System.DateTime Date {
+ get {
+ System.DateTime time;
+ try {
+#if USE_TIFF
+ SubdirectoryEntry sub = (SubdirectoryEntry) ExifHeader.Directory.Lookup (TagId.ExifIfdPointer);
+ DirectoryEntry e;
+
+ if (sub != null) {
+ e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
+
+ if (e != null)
+ return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
+ }
+
+ e = ExifHeader.Directory.Lookup (TagId.DateTime);
+
+ if (e != null)
+ return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
+
+ return base.Date;
+#else
+ string time_str = "";
+ time_str = ExifData.LookupFirstValue (Exif.Tag.DateTimeOriginal);
+
+ if (time_str == null || time_str == "")
+ time_str = ExifData.LookupFirstValue (Exif.Tag.DateTime);
+
+ time = Exif.ExifUtil.DateTimeFromString (time_str).ToUniversalTime ();
+#endif
+ } catch (System.Exception e) {
+ Console.WriteLine (e);
+ time = base.Date;
+ }
+ return time;
+ }
+ }
+
+#if ENABLE_NUNIT
+ [TestFixture]
+ public class Tests {
+ public Tests ()
+ {
+ Gnome.Vfs.Vfs.Initialize ();
+ Gtk.Application.Init ();
+ }
+
+#if false
+ [Test]
+ public void TestLoad ()
+ {
+ JpegFile jimg = new JpegFile ("/home/lewing/start.swe.jpeg");
+ Assert.AreEqual (PixbufOrientation.TopLeft, jimg.Orientation);
+ }
+#endif
+ [Test]
+ public void TestSave ()
+ {
+ string desc = "this is an example description";
+ string desc2 = "\x00a9 Novell Inc.";
+ PixbufOrientation orient = PixbufOrientation.TopRight;
+ Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
+ string path = ImageFile.TempPath ("joe.jpg");
+
+ PixbufUtils.SaveJpeg (test, path, 75, new Exif.ExifData ());
+ JpegFile jimg = new JpegFile (path);
+ jimg.SetDescription (desc);
+ jimg.SetOrientation (orient);
+ jimg.SaveMetaData (path);
+ JpegFile mod = new JpegFile (path);
+ Assert.AreEqual (mod.Orientation, orient);
+ Assert.AreEqual (mod.Description, desc);
+ jimg.SetDescription (desc2);
+ jimg.SaveMetaData (path);
+ mod = new JpegFile (path);
+ Assert.AreEqual (mod.Description, desc2);
+
+ File.Delete (path);
+ }
+ }
+#endif
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/JpegHeader.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/JpegHeader.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,731 @@
+/*
+ * Copyright (c) 2006 Novell Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using System.IO;
+using System.Collections;
+using FSpot.Xmp;
+using FSpot;
+
+#if ENABLE_NUNIT
+using NUnit.Framework;
+#endif
+
+public class JpegHeader : SemWeb.StatementSource {
+ public enum JpegMarker {
+ Tem = 0x01,
+ Rst0 = 0xd0, // RstN used for resync, ignore
+ Rst1 = 0xd1,
+ Rst2 = 0xd2,
+ Rst3 = 0xd3,
+ Rst4 = 0xd4,
+ Rst5 = 0xd5,
+ Rst6 = 0xd6,
+ Rst7 = 0xd7,
+
+ Sof0 = 0xc0, // SOFn Start of frame 0-1 common
+ Sof1 = 0xc1,
+ Sof2 = 0xc2,
+ Sof3 = 0xc3,
+
+ Dht = 0xc4, // Define Huffman Table
+
+ Sof5 = 0xc5,
+ Sof6 = 0xc6,
+ Sof7 = 0xc7,
+
+ Jpg = 0xc8, // reserved
+
+ Sof9 = 0xc9,
+ Sof10 = 0xca,
+ Sof11 = 0xcb,
+ Sof12 = 0xcc,
+ Sof13 = 0xcd,
+ Sof14 = 0xce,
+ Sof15 = 0xcf,
+
+ // These tags all consist of a marker and then a length.
+
+ // These are the major structure tags.
+ Soi = 0xd8, // Start of Image
+ Eoi = 0xd9, // End of Image
+ Sos = 0xda, // Start of Scan
+
+ Dnl = 0xdc,
+ Dri = 0xdd, // Define restart interval
+ Dhp = 0xde,
+ Exp = 0xdf,
+
+ Dqt = 0xdb, // Define Quantization Table
+
+ // These are the app marker tags that contain the application metadata
+ // in its various forms.
+ App0 = 0xe0, // AppN Markers for application data
+ App1 = 0xe1,
+ App2 = 0xe2,
+ App3 = 0xe3,
+ App4 = 0xe4,
+ App5 = 0xe5,
+ App6 = 0xe6,
+ App7 = 0xe7,
+ App8 = 0xe8,
+ App9 = 0xe9,
+ App10 = 0xea,
+ App11 = 0xeb,
+ App12 = 0xec,
+ App13 = 0xed,
+ App14 = 0xee,
+ App15 = 0xef,
+
+ Jpg0 = 0xf0,
+ Jpg1 = 0xf1,
+ Jpg2 = 0xf2,
+ Jpg3 = 0xf3,
+ Jpg4 = 0xf4,
+ Jpg5 = 0xf5,
+ Jpg6 = 0xf6,
+ Jpg7 = 0xf7,
+ Jpg8 = 0xf8,
+ Jpg9 = 0xf9,
+ Jpg10 = 0xfa,
+ Jpg11 = 0xfb,
+ Jpg12 = 0xfc,
+ Jpg13 = 0xfd,
+
+ Com = 0xfe // Comment
+ }
+
+ private System.Collections.ArrayList marker_list = new System.Collections.ArrayList ();
+ private byte [] image_data;
+
+ // False seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+
+ public class Marker {
+ public JpegMarker Type;
+ public byte [] Data;
+
+ public Marker (JpegMarker type, byte [] data)
+ {
+ this.Type = type;
+ this.Data = data;
+ }
+
+ public bool IsApp {
+ get {
+ return (this.Type >= JpegMarker.App0 && this.Type <= JpegMarker.App15);
+ }
+ }
+
+ public bool Matches (Signature sig)
+ {
+ if (Type == sig.Id) {
+ if (sig.Name == null)
+ return true;
+
+ byte [] name = System.Text.Encoding.ASCII.GetBytes (sig.Name);
+
+ for (int i = 0; i < name.Length; i++)
+ if (Data [i] != name [i])
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ public string GetName ()
+ {
+ if (!this.IsApp)
+ return null;
+
+ int j;
+ for (j = 0; j < this.Data.Length; j++) {
+ if (this.Data [j] == 0x00)
+ break;
+
+ }
+
+ if (j > 0)
+ return System.Text.Encoding.ASCII.GetString (this.Data, 0, j);
+ else
+ return null;
+ }
+
+ private static int Read (Stream stream, byte [] dest, int start, int len)
+ {
+ int pos = 0;
+
+ while (pos < len) {
+ int read = stream.Read (dest, pos + start, len - pos);
+ if (read <= 0)
+ break;
+
+ pos += read;
+ }
+ return pos;
+ }
+
+ public static Marker Load (Stream stream) {
+ byte [] raw = new byte [2];
+ ushort length;
+
+ if (stream.Length - stream.Position < 2)
+ return null;
+
+ // FIXME there is a potential loop here.
+
+ int read = Read (stream, raw, 0, 2);
+ if (read < 2 || raw [0] != 0xff)
+ throw new System.Exception (System.String.Format ("Invalid marker found {0}", raw [0]));
+
+ JpegMarker id = (JpegMarker) raw [1];
+ switch (id) {
+ case JpegMarker.Soi:
+ case JpegMarker.Eoi:
+ case JpegMarker.Rst0:
+ case JpegMarker.Rst1:
+ case JpegMarker.Rst2:
+ case JpegMarker.Rst3:
+ case JpegMarker.Rst4:
+ case JpegMarker.Rst5:
+ case JpegMarker.Rst6:
+ case JpegMarker.Rst7:
+ case JpegMarker.Tem:
+ case (JpegMarker) 0:
+ return new Marker (id, null);
+ default:
+ Read (stream, raw, 0, 2);
+ length = FSpot.BitConverter.ToUInt16 (raw, 0, false);
+
+ byte [] data = new byte [length - 2];
+ Read (stream, data, 0, data.Length);
+ return new Marker (id, data);
+ }
+
+ }
+
+ public void Save (System.IO.Stream stream) {
+ /*
+ * It is possible we should just base this choice off the existance
+ * of this.Data, but I'm not sure so I'll do it this way for now
+ */
+
+ switch (this.Type) {
+ case JpegMarker.Soi:
+ case JpegMarker.Eoi:
+ stream.WriteByte (0xff);
+ stream.WriteByte ((byte)this.Type);
+ break;
+ default:
+ stream.WriteByte (0xff);
+ stream.WriteByte ((byte)this.Type);
+ ushort length = (ushort)(this.Data.Length + 2);
+
+ byte [] len = FSpot.BitConverter.GetBytes (length, false);
+ stream.Write (len, 0, len.Length);
+
+ //workaround for mono bug: http://bugzilla.ximian.com/show_bug.cgi?id=82836
+ if (this.Data.Length > 0)
+ stream.Write (this.Data, 0, this.Data.Length);
+ break;
+ }
+ }
+ }
+
+ public static Signature JfifSignature = new Signature (JpegMarker.App0, "JFIF\0");
+ public static Signature JfxxSignature = new Signature (JpegMarker.App0, "JFXX\0");
+ public static Signature XmpSignature = new Signature (JpegMarker.App1, "http://ns.adobe.com/xap/1.0/\0");
+ public static Signature ExifSignature = new Signature (JpegMarker.App1, "Exif\0\0");
+ public static Signature IccProfileSignature = new Signature (JpegMarker.App2, "ICC_PROFILE\0");
+ public static Signature PhotoshopSignature = new Signature (JpegMarker.App13, "Photoshop 3.0\0");
+ public static Signature QuantizationSignature = new Signature (JpegMarker.Dqt, null);
+
+ public class Signature {
+ public JpegMarker Id;
+ public string Name;
+
+ public Signature (JpegMarker marker, string name)
+ {
+ Id = marker;
+ Name = name;
+ }
+
+ public int WriteName (Stream stream)
+ {
+ byte [] sig = System.Text.Encoding.ASCII.GetBytes (Name);
+ stream.Write (sig, 0, sig.Length);
+ return sig.Length;
+ }
+ }
+
+ public Marker FindMarker (Signature sig)
+ {
+ foreach (Marker m in Markers) {
+ if (m.Matches (sig))
+ return m;
+ }
+
+ return null;
+ }
+
+
+ public Marker FindMarker (JpegMarker id, string name)
+ {
+ return FindMarker (new Signature (id, name));
+ }
+
+#if false
+ public Cms.Profile GetProfile ()
+ {
+ Marker m = FindMarker (IccProfileSignature);
+ string name = IccProfileSignature.Name;
+ try {
+ if (m != null)
+ return new Cms.Profile (m.Data, name.Length, m.Data.Length - name.Length);
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e);
+ }
+
+ FSpot.Tiff.Header exif = GetExifHeader ();
+ if (exif != null)
+ return exif.Directory.GetProfile ();
+
+ return null;
+ }
+#endif
+ public FSpot.Tiff.Header GetExifHeader ()
+ {
+ string name = ExifSignature.Name;
+ Marker marker = FindMarker (ExifSignature);
+
+ if (marker == null)
+ return null;
+
+ using (System.IO.Stream exifstream = new System.IO.MemoryStream (marker.Data, name.Length, marker.Data.Length - name.Length, false)) {
+ FSpot.Tiff.Header exif = new FSpot.Tiff.Header (exifstream);
+ return exif;
+ }
+ }
+
+ public XmpFile GetXmp ()
+ {
+ string name = XmpSignature.Name;
+ Marker marker = FindMarker (XmpSignature);
+ if (marker != null) {
+ int len = name.Length;
+ //System.Console.WriteLine (System.Text.Encoding.ASCII.GetString (marker.Data, len,
+ // marker.Data.Length - len));
+ using (System.IO.Stream xmpstream = new System.IO.MemoryStream (marker.Data, len,
+ marker.Data.Length - len, false)) {
+
+ XmpFile xmp = new XmpFile (xmpstream);
+ return xmp;
+ }
+ }
+ return null;
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ FSpot.Tiff.Header exif = GetExifHeader ();
+ if (exif != null)
+ exif.Select (sink);
+
+ XmpFile xmp = GetXmp ();
+ if (xmp != null)
+ xmp.Select (sink);
+
+ string name = PhotoshopSignature.Name;
+ JpegHeader.Marker marker = FindMarker (PhotoshopSignature);
+ if (marker != null) {
+ int len = name.Length;
+ using (System.IO.Stream bimstream = new System.IO.MemoryStream (marker.Data, len, marker.Data.Length - len, false)) {
+ FSpot.Bim.BimFile bim = new FSpot.Bim.BimFile (bimstream);
+ bim.Select (sink);
+ }
+ }
+ }
+
+ public Exif.ExifData Exif {
+ get {
+ Marker m = FindMarker (ExifSignature);
+
+ if (m == null)
+ return null;
+
+ return new Exif.ExifData (m.Data);
+ }
+ }
+
+ public void Replace (Signature sig, Marker data)
+ {
+ bool added = false;
+ for (int i = 1; i < Markers.Count; i++) {
+ Marker m = (Marker) Markers [i];
+
+ if (m.Matches (sig)) {
+
+ if (!added) {
+ Markers [i] = data;
+ added = true;
+ } else
+ Markers.RemoveAt (i--);
+
+ } else if (!m.IsApp || m.Type > sig.Id) {
+ if (!added) {
+ Markers.Insert (i, data);
+ added = true;
+ }
+ }
+ }
+
+ if (!added)
+ throw new System.Exception (String.Format ("unable to replace {0} marker", sig.Name));
+ }
+
+ public void SetExif (Exif.ExifData value)
+ {
+ // Console.WriteLine ("before save");
+ byte [] raw_data = value.Save ();
+ // Console.WriteLine ("saved");
+ Marker exif = new Marker (ExifSignature.Id, raw_data);
+ // Console.WriteLine ("new");
+ Replace (ExifSignature, exif);
+ // Console.WriteLine ("replaced");
+ }
+
+ public void SetXmp (XmpFile xmp)
+ {
+ using (MemoryStream stream = new MemoryStream ()) {
+
+ XmpSignature.WriteName (stream);
+ xmp.Save (stream);
+
+ Marker xmp_marker = new Marker (XmpSignature.Id, stream.ToArray ());
+ Replace (XmpSignature, xmp_marker);
+ }
+ }
+
+ public System.Collections.ArrayList Markers {
+ get {
+ return marker_list;
+ }
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ foreach (Marker marker in marker_list) {
+ // System.Console.WriteLine ("saving marker {0} {1}", marker.Type,
+ // (marker.Data != null) ? marker.Data.Length .ToString (): "(null)");
+ marker.Save (stream);
+ if (marker.Type == JpegMarker.Sos)
+ stream.Write (ImageData, 0, ImageData.Length);
+ }
+ }
+
+ public JpegHeader (System.IO.Stream stream)
+ {
+ Load (stream, false);
+ }
+
+ public JpegHeader (System.IO.Stream stream, bool metadata_only)
+ {
+ try {
+ Load (stream, metadata_only);
+ } catch (System.Exception e) {
+ Console.WriteLine ("Exeption while reading jpeg headers");
+ Console.WriteLine(e);
+ }
+ }
+
+ private void Load (System.IO.Stream stream, bool metadata_only)
+ {
+ marker_list.Clear ();
+ image_data = null;
+ bool at_image = false;
+
+ Marker marker = Marker.Load (stream);
+ if (marker.Type != JpegMarker.Soi)
+ throw new System.Exception ("This doesn't appear to be a jpeg stream");
+
+ this.Markers.Add (marker);
+ while (!at_image) {
+ marker = Marker.Load (stream);
+
+ if (marker == null)
+ break;
+
+ // System.Console.WriteLine ("loaded marker {0} length {1}", marker.Type, marker.Data.Length);
+
+ this.Markers.Add (marker);
+
+ if (marker.Type == JpegMarker.Sos) {
+ at_image = true;
+
+ if (metadata_only) {
+ // System.Console.WriteLine ("read = {0}", stream.Position);
+ return;
+ }
+ }
+ }
+
+ long image_data_length = stream.Length - stream.Position;
+ this.image_data = new byte [image_data_length];
+
+ if (stream.Read (image_data, 0, (int)image_data_length) != image_data_length)
+ throw new System.Exception ("truncated image data or something");
+ }
+
+ static int [] StandardLuminanceQuantization = new int [] {
+ 16, 11, 12, 14, 12, 10, 16, 14,
+ 13, 14, 18, 17, 16, 19, 24, 40,
+ 26, 24, 22, 22, 24, 49, 35, 37,
+ 29, 40, 58, 51, 61, 60, 57, 51,
+ 56, 55, 64, 72, 92, 78, 64, 68,
+ 87, 69, 55, 56, 80, 109, 81, 87,
+ 95, 98, 103, 104, 103, 62, 77, 113,
+ 121, 112, 100, 120, 92, 101, 103, 99
+ };
+
+ static int [] StandardChrominanceQuantization = new int [] {
+ 17, 18, 18, 24, 21, 24, 47, 26,
+ 26, 47, 99, 66, 56, 66, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+ };
+
+ /*
+ * GuessQuality is taken from the jpegdump utility
+ * Copyright (c) 1992 Handmade Software, Inc.
+ * by Allan N. Hessenflow licenced as GPL with the authors
+ * permission. Many Thanks.
+ */
+ public int GuessQuality ()
+ {
+ Marker dqt = FindMarker (QuantizationSignature);
+ int quality = 0;
+ int position = 0;
+
+ while (position < dqt.Data.Length) {
+ int tableindex;
+ int [] table = null;
+ double cumsf = 0.0;
+ double cumsf2 = 0.0;
+ bool allones = true;
+ int row, col;
+
+ tableindex = dqt.Data [position ++];
+
+ switch (tableindex & 0x0f) {
+ case 0:
+ table = StandardLuminanceQuantization;
+ break;
+ case 1:
+ table = StandardChrominanceQuantization;
+ break;
+ default:
+ table = null;
+ break;
+ }
+
+ for (row=0; row<8; row++) {
+ for (col=0; col<8; col++) {
+ uint val;
+
+ if ((tableindex >> 4) > 0) {
+ val = FSpot.BitConverter.ToUInt16 (dqt.Data, position, false);
+ position += 2;
+ } else
+ val = (uint) dqt.Data [position ++];
+
+ if (table != null) {
+ double x;
+
+ /* scaling factor in percent */
+ x = 100.0 * (double)val / (double)table [row*8+col];
+ cumsf += x;
+ cumsf2 += x * x;
+
+ /* separate check for all-ones table (Q 100) */
+ if (val != 1)
+ allones = false;
+ }
+ }
+ }
+
+ if (table != null) {
+ double local_quality;
+
+ cumsf /= 64.0; /* mean scale factor */
+ cumsf2 /= 64.0;
+
+ //double variance;
+ //variance = cumsf2 - (cumsf * cumsf);
+
+ if (allones) /* special case for all-ones table */
+ local_quality = 100.0;
+ else if (cumsf <= 100.0)
+ local_quality = (200.0 - cumsf) / 2.0;
+ else
+ local_quality = 5000.0 / cumsf;
+
+ quality = Math.Max (quality, (int)local_quality);
+ }
+ }
+ return quality;
+ }
+
+ public byte [] ImageData {
+ get {
+ return image_data;
+ }
+ }
+
+#if ENABLE_NUNIT
+ [TestFixture]
+ public class Tests {
+ int quality = 75;
+
+ public string CreateFile ()
+ {
+ Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
+ string path = FSpot.ImageFile.TempPath ("joe.jpg");
+ string desc = "\x00a9 Novell Inc.";
+ PixbufOrientation orient = PixbufOrientation.TopRight;
+
+ PixbufUtils.SaveJpeg (test, path, quality, new Exif.ExifData ());
+ FSpot.JpegFile jimg = new FSpot.JpegFile (path);
+ jimg.SetDescription (desc);
+ jimg.SetOrientation (orient);
+ jimg.SaveMetaData (path);
+
+ return path;
+ }
+
+ [Test]
+ public void Load ()
+ {
+ string path = CreateFile ();
+
+ using (Stream stream = File.OpenRead (path)) {
+ JpegHeader jhead = new JpegHeader (stream);
+
+ Assert.AreEqual (((Marker)jhead.Markers [0]).Type, JpegMarker.Soi);
+ Assert.AreEqual (((Marker)jhead.Markers [1]).GetName (), "JFIF");
+ Assert.AreEqual (((Marker)jhead.Markers [1]).Type, JpegMarker.App0);
+ Assert.AreEqual (((Marker)jhead.Markers [2]).GetName (), "Exif");
+ Assert.AreEqual (((Marker)jhead.Markers [2]).Type, JpegMarker.App1);
+
+ // NOTE the currently we don't store the Eoi as the last marker
+ Assert.AreEqual (((Marker)jhead.Markers [jhead.Markers.Count -1]).Type, JpegMarker.Sos);
+
+ // NOTE this is kind of sill but it might help
+ Assert.IsTrue (Math.Abs (jhead.GuessQuality () - quality) <= 1);
+
+ Assert.IsNotNull (jhead.GetExifHeader ());
+ }
+
+ File.Delete (path);
+ }
+
+ [Test]
+ public void Save ()
+ {
+ string in_path = CreateFile ();
+ string out_path = ImageFile.TempPath ("output.jpg");
+ JpegHeader source;
+ JpegHeader dest;
+
+ using (Stream orig = File.OpenRead (in_path)) {
+ source = new JpegHeader (orig);
+
+ using (Stream output = File.OpenWrite (out_path)) {
+ source.Save (output);
+ }
+
+ using (Stream result = File.OpenRead (out_path)) {
+ dest = new JpegHeader (result);
+
+ Assert.AreEqual (source.Markers.Count, dest.Markers.Count);
+ Assert.AreEqual (source.GuessQuality (), dest.GuessQuality ());
+ Assert.AreEqual (orig.Length, result.Length);
+ for (int i = 0; i < source.Markers.Count; i++) {
+ Marker d = (Marker) dest.Markers [i];
+ Marker s = (Marker) source.Markers [i];
+
+ Assert.AreEqual (d.Type, s.Type);
+ Assert.AreEqual (d.GetName (), s.GetName ());
+
+ if (d.Data != null) {
+ Assert.AreEqual (d.Data.Length, s.Data.Length);
+
+ for (int j = 0; j < d.Data.Length; j++) {
+ Assert.AreEqual (d.Data [j], s.Data [j]);
+ }
+ } else {
+ Assert.AreEqual (d.Data, s.Data);
+ }
+ }
+ }
+ }
+
+ File.Delete (in_path);
+ File.Delete (out_path);
+ }
+ }
+#endif
+
+#if false
+ public static int Main (string [] args)
+ {
+ JpegHeader data = new JpegHeader (args [0]);
+ byte [] value = data.GetRawXmp ();
+
+ if (value != null) {
+ string xml = System.Text.Encoding.UTF8.GetString (value, 29, value.Length - 29);
+ System.Console.WriteLine (xml);
+ }
+
+ value = data.GetRaw ("ICC_PROFILE");
+ if (value != null) {
+ System.IO.FileStream stream = new System.IO.FileStream ("profile.icc", System.IO.FileMode.Create);
+ stream.Write (value, 12, value.Length - 12);
+ stream.Close ();
+ }
+
+ value = data.GetRawExif ();
+
+
+ //System.IO.Stream ostream = System.IO.File.Open ("/home/lewing/test.jpg", System.IO.FileMode.OpenOrCreate);
+ //data.Save (ostream);
+ //ostream.Position = 0;
+ //data = new JpegHeader (ostream);
+
+ return 0;
+ }
+#endif
+}
Added: trunk/beagle/Util/F-Spot/Imaging/JpegUtils.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/JpegUtils.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,67 @@
+using System.Runtime.InteropServices;
+using System;
+using Gdk;
+
+public class JpegUtils {
+ [DllImport ("libfspot")]
+ static extern IntPtr f_load_scaled_jpeg (string path,
+ int target_width,
+ int target_height,
+ out int original_width_return,
+ out int original_height_return);
+
+ /* FIXME */
+ [DllImport("libgobject-2.0-0.dll")]
+ static extern void g_object_unref (IntPtr raw);
+
+ public static Pixbuf LoadScaled (string path, int target_width, int target_height,
+ out int original_width, out int original_height)
+ {
+ Pixbuf pixbuf = new Pixbuf (f_load_scaled_jpeg (path, target_width, target_height,
+ out original_width, out original_height));
+ g_object_unref (pixbuf.Handle);
+ return pixbuf;
+ }
+
+ public static Pixbuf LoadScaled (string path, int target_width, int target_height)
+ {
+ int unused;
+ return LoadScaled (path, target_width, target_height, out unused, out unused);
+ }
+
+ [DllImport ("libfspot")]
+ static extern void f_save_jpeg_exif (string path, HandleRef data);
+
+ public static void SaveExif (string path, Exif.ExifData data)
+ {
+ f_save_jpeg_exif (path, data.Handle);
+ }
+
+ [DllImport ("libfspot")]
+ static extern void f_get_jpeg_size (string path, out int width_return, out int height_return);
+
+ public static void GetSize (string path, out int width_return, out int height_return)
+ {
+ f_get_jpeg_size (path, out width_return, out height_return);
+ }
+
+ public enum TransformType {
+ Rotate90,
+ Rotate180,
+ Rotate270,
+ FlipH,
+ FlipV
+ };
+
+ [DllImport ("libfspot")]
+ static extern bool f_transform_jpeg (string source_path, string destination_path, TransformType transform,
+ out string error_message_return);
+
+ public static void Transform (string source_path, string destination_path, TransformType transform)
+ {
+ string error_message;
+
+ if (! f_transform_jpeg (source_path, destination_path, transform, out error_message))
+ throw new Exception (error_message);
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/MrwFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/MrwFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,253 @@
+using FSpot.Tiff;
+
+namespace FSpot.Mrw {
+ // Minolta raw format
+ // see http://www.dalibor.cz/minolta/raw_file_format.htm for details
+ // note that the blocks can be in any order.
+
+ public class Block {
+ protected byte [] name;
+ protected uint Length;
+ protected long Start;
+ protected System.IO.Stream stream;
+ byte [] data;
+
+ public Block (System.IO.Stream stream)
+ {
+ this.stream = stream;
+ Start = stream.Position;
+ name = new byte [4];
+ byte [] tmp = new byte [8];
+ stream.Read (tmp, 0, tmp.Length);
+ System.Array.Copy (tmp, name, name.Length);
+ System.Console.WriteLine (this.Name);
+ Length = BitConverter.ToUInt32 (tmp, name.Length, false);
+ stream.Position = stream.Position + Length;
+ }
+
+ public string Name {
+ get {
+ return System.Text.Encoding.ASCII.GetString (this.name, 1, 3);
+ }
+ }
+
+ public byte [] Data {
+ get {
+ if (data == null)
+ data = ReadData ();
+
+ return data;
+ }
+ }
+
+ public static Block Create (System.IO.Stream stream)
+ {
+ byte [] tmp = new byte [4];
+ stream.Read (tmp, 0, tmp.Length);
+ stream.Position -= 4;
+ string name = System.Text.Encoding.ASCII.GetString (tmp, 1, 3);
+ switch (name) {
+ case "TTW":
+ return new TtwBlock (stream);
+ case "PRD":
+ return new PrdBlock (stream);
+ default:
+ return new Block (stream);
+ }
+ }
+
+ protected byte [] ReadData ()
+ {
+ stream.Position = Start + 8;
+ byte [] data = new byte [this.Length];
+ stream.Read (data, 0, data.Length);
+
+ return data;
+ }
+ }
+
+ public class PrdBlock : Block {
+ public PrdBlock (System.IO.Stream stream) : base (stream)
+ {
+
+ }
+
+ public ulong Version {
+ get {
+ return BitConverter.ToUInt64 (this.Data, 0, false);
+ }
+ }
+
+ public ushort CCDSizeY {
+ get {
+ return BitConverter.ToUInt16 (this.Data, 8, false);
+ }
+ }
+
+ public ushort CCDSizeX {
+ get {
+ return BitConverter.ToUInt16 (this.Data, 10, false);
+ }
+ }
+
+ public ushort ImageSizeY {
+ get {
+ return BitConverter.ToUInt16 (this.Data, 12, false);
+ }
+ }
+
+ public ushort ImageSizeX {
+ get {
+ return BitConverter.ToUInt16 (this.Data, 14, false);
+ }
+ }
+
+ public byte Depth {
+ get {
+ return this.Data [16];
+ }
+ }
+
+ public byte SampleDepth {
+ get {
+ return this.Data [17];
+ }
+ }
+ }
+
+ internal class TtwBlock : Block {
+ FSpot.Tiff.Header header;
+
+ public TtwBlock (System.IO.Stream stream) : base (stream)
+ {
+ if (this.Name != "TTW")
+ throw new System.Exception (System.String.Format ("invalid block name {0}", this.Name));
+ }
+
+ public FSpot.Tiff.Header TiffHeader {
+ get {
+ if (header == null) {
+ try {
+ System.IO.MemoryStream mem = new System.IO.MemoryStream (this.Data);
+ System.Console.WriteLine ("before header");
+ header = new Header (mem);
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+
+ return header;
+ }
+ }
+ }
+
+ internal class MrmBlock : Block {
+ Block [] blocks;
+
+ public MrmBlock (System.IO.Stream stream) : base (stream) {}
+
+ protected void Load ()
+ {
+ stream.Position = Start + 8;
+ System.Collections.ArrayList list = new System.Collections.ArrayList ();
+
+ while (stream.Position < Start + 8 + Length) {
+ list.Add (Block.Create (stream));
+ }
+ blocks = (Block []) list.ToArray (typeof (Block));
+ }
+
+ public Block [] Blocks {
+ get {
+ if (blocks == null) {
+ Load ();
+ }
+
+ return blocks;
+ }
+ }
+
+ }
+
+ public class MrwFile : ImageFile, SemWeb.StatementSource {
+ MrmBlock mrm;
+ FSpot.Tiff.Header header;
+
+ public MrwFile (System.Uri uri) : base (uri)
+ {
+ }
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public MrwFile (string path) : base (path)
+ {
+ }
+
+ public FSpot.Tiff.Header Header {
+ get {
+ if (mrm == null)
+ LoadBlocks ();
+
+ return header;
+ }
+ }
+
+ public override System.DateTime Date
+ {
+ get {
+ DirectoryEntry e = this.Header.Directory.Lookup (TagId.DateTime);
+
+ if (e != null)
+ return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
+ else
+ return base.Date;
+ }
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ this.Header.Select (sink);
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+#if false
+ public override Gdk.Pixbuf Load ()
+ {
+ using (System.IO.Stream stream = Open ()) {
+ return new Gdk.Pixbuf (PixbufStream ());
+ }
+ }
+
+ public override Gdk.Pixbuf Load (int width, int height)
+ {
+ return PixbufUtils.ScaleToMaxSize (this.Load (), width, height);
+ }
+#endif
+ protected void LoadBlocks ()
+ {
+ using (System.IO.Stream file = Open ()) {
+ mrm = new MrmBlock (file);
+ try {
+ foreach (Block b in mrm.Blocks) {
+ if (b is TtwBlock) {
+ TtwBlock ttw = (TtwBlock) b;
+ header = ttw.TiffHeader;
+ //Header.Dump ("TTW:");
+ break;
+ }
+ }
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+ }
+ }
+
+}
Added: trunk/beagle/Util/F-Spot/Imaging/OrderedWriter.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/OrderedWriter.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,35 @@
+using System.IO;
+
+namespace FSpot {
+ public class OrderedWriter {
+ Stream stream;
+ bool is_little;
+
+ public Stream Stream {
+ get { return stream; }
+ }
+
+ public OrderedWriter (Stream stream, bool is_little)
+ {
+ this.stream = stream;
+ this.is_little = is_little;
+ }
+
+ public void Write (byte b)
+ {
+ stream.WriteByte (b);
+ }
+
+ public void Write (uint val)
+ {
+ byte [] value = FSpot.BitConverter.GetBytes (val, is_little);
+ stream.Write (value, 0, value.Length);
+ }
+
+ public void Write (ushort val)
+ {
+ byte [] value = FSpot.BitConverter.GetBytes (val, is_little);
+ stream.Write (value, 0, value.Length);
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/PngFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/PngFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,1551 @@
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using SemWeb;
+using System.IO;
+using FSpot.Xmp;
+using System.Collections;
+using System.Reflection;
+
+#if ENABLE_NUNIT
+using NUnit.Framework;
+#endif
+
+namespace FSpot.Png {
+ public class PngFile : ImageFile, SemWeb.StatementSource {
+ PngHeader header;
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ private PngHeader Header {
+ get {
+ if (header == null) {
+ using (System.IO.Stream input = Open ()) {
+ header = new PngHeader (input);
+ }
+ }
+
+ return header;
+ }
+ }
+
+ public System.Collections.ArrayList Chunks {
+ get { return Header.Chunks; }
+ }
+
+ public PngFile (System.Uri uri) : base (uri)
+ {
+ }
+
+ public PngFile (string path) : base (path)
+ {
+ }
+
+
+ /**
+ Title Short (one line) title or caption for image
+ Author Name of image's creator
+ Description Description of image (possibly long)
+ Copyright Copyright notice
+ Creation Time Time of original image creation
+ Software Software used to create the image
+ Disclaimer Legal disclaimer
+ Warning Warning of nature of content
+ Source Device used to create the image
+ Comment Miscellaneous comment
+
+ xmp is XML:com.adobe.xmp
+
+ Other keywords may be defined for other purposes. Keywords of general interest can be registered with th
+ */
+ public void Select (SemWeb.StatementSink sink)
+ {
+ foreach (Chunk c in Chunks) {
+ if (c is IhdrChunk) {
+ IhdrChunk ih = c as IhdrChunk;
+ MetadataStore.AddLiteral (sink, "tiff:ImageWidth", ih.Width.ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageLength", ih.Height.ToString ());
+ } else if(c is TimeChunk) {
+ TimeChunk tc = c as TimeChunk;
+
+ MetadataStore.AddLiteral (sink, "xmp:ModifyDate", tc.Time.ToString ("yyyy-MM-ddThh:mm:ss"));
+ } else if (c is TextChunk) {
+ TextChunk text = c as TextChunk;
+
+ switch (text.Keyword) {
+ case "XMP":
+ case "XML:com.adobe.xmp":
+ using (System.IO.Stream xmpstream = new System.IO.MemoryStream (text.TextData)) {
+ FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
+ xmp.Select (sink);
+ }
+ break;
+ case "Comment":
+ MetadataStore.AddLiteral (sink, "exif:UserComment", text.Text);
+ break;
+ case "Software":
+ MetadataStore.AddLiteral (sink, "xmp:CreatorTool", text.Text);
+ break;
+ case "Title":
+ MetadataStore.AddLiteral (sink, "dc:title", "rdf:Alt", new SemWeb.Literal (text.Text, "x-default", null));
+ break;
+ case "Author":
+ MetadataStore.AddLiteral (sink, "dc:creator", "rdf:Seq", new SemWeb.Literal (text.Text));
+ break;
+ case "Copyright":
+ MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt", new SemWeb.Literal (text.Text, "x-default", null));
+ break;
+ case "Description":
+ MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt", new SemWeb.Literal (text.Text, "x-default", null));
+ break;
+ case "Creation Time":
+ try {
+ System.DateTime time = System.DateTime.Parse (text.Text);
+ MetadataStore.AddLiteral (sink, "xmp:CreateDate", time.ToString ("yyyy-MM-ddThh:mm:ss"));
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ break;
+ }
+ } else if (c is ColorChunk) {
+ ColorChunk color = (ColorChunk)c;
+ string [] whitepoint = new string [2];
+ whitepoint [0] = color.WhiteX.ToString ();
+ whitepoint [1] = color.WhiteY.ToString ();
+ MetadataStore.Add (sink, "tiff:WhitePoint", "rdf:Seq", whitepoint);
+ int i = 0;
+ string [] rgb = new string [6];
+ rgb [i++] = color.RedX.ToString ();
+ rgb [i++] = color.RedY.ToString ();
+ rgb [i++] = color.GreenX.ToString ();
+ rgb [i++] = color.GreenY.ToString ();
+ rgb [i++] = color.BlueX.ToString ();
+ rgb [i++] = color.BlueY.ToString ();
+ MetadataStore.Add (sink, "tiff:PrimaryChromaticities", "rdf:Seq", rgb);
+ } else if (c.Name == "sRGB") {
+ MetadataStore.AddLiteral (sink, "exif:ColorSpace", "1");
+ } else if (c is PhysChunk) {
+ PhysChunk phys = (PhysChunk)c;
+ uint denominator = (uint) (phys.InMeters ? 100 : 1);
+
+ MetadataStore.AddLiteral (sink, "tiff:ResolutionUnit", phys.InMeters ? "3" : "1");
+ MetadataStore.AddLiteral (sink, "tiff:XResolution", new FSpot.Tiff.Rational (phys.PixelsPerUnitX, denominator).ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:YResolution", new FSpot.Tiff.Rational (phys.PixelsPerUnitY, denominator).ToString ());
+ }
+ }
+ }
+
+ public class ZtxtChunk : TextChunk {
+ //public static string Name = "zTXt";
+
+ protected bool compressed = true;
+ public bool Compressed {
+ get {
+ return compressed;
+ }
+ }
+
+ byte compression;
+ public byte Compression {
+ get {
+ return compression;
+ }
+ set {
+ if (compression != 0)
+ throw new System.Exception ("Unknown compression method");
+ }
+ }
+
+ public ZtxtChunk (string keyword, string text) : base ()
+ {
+ Name = "zTXt";
+ Compression = 0;
+ this.keyword = keyword;
+ }
+
+ public ZtxtChunk (string name, byte [] data) : base (name, data)
+ {
+ }
+
+ protected ZtxtChunk ()
+ {
+ }
+
+ public override void SetText (string text)
+ {
+ /* FIXME this is broken */
+ text_data = encoding.GetBytes (text);
+ data = Chunk.Deflate (text_data, 0, text_data.Length);
+ }
+
+ public override void Load (byte [] data)
+ {
+ int i = 0;
+ keyword = GetString (ref i);
+ i++;
+ Compression = data [i++];
+
+ text_data = Chunk.Inflate (data, i, data.Length - i);
+ }
+ }
+
+ public class PhysChunk : Chunk {
+ public PhysChunk (string name, byte [] data) : base (name, data) {}
+
+ public uint PixelsPerUnitX {
+ get {
+ return BitConverter.ToUInt32 (data, 0, false);
+ }
+ }
+
+ public uint PixelsPerUnitY {
+ get {
+ return BitConverter.ToUInt32 (data, 4, false);
+ }
+ }
+
+ public bool InMeters {
+ get {
+ return data [8] == 0;
+ }
+ }
+ }
+
+ public class TextChunk : Chunk {
+ //public static string Name = "tEXt";
+
+ protected string keyword;
+ protected byte [] text_data;
+ protected System.Text.Encoding encoding = Latin1;
+
+ public static System.Text.Encoding Latin1 = System.Text.Encoding.GetEncoding (28591);
+
+ public TextChunk (string name, byte [] data) : base (name, data)
+ {
+ }
+
+ protected TextChunk ()
+ {
+ }
+
+
+ public TextChunk (string keyword, string text)
+ {
+ this.Name = "tEXt";
+ this.keyword = keyword;
+ SetText (text);
+ }
+
+ public override void Load (byte [] data)
+ {
+ int i = 0;
+
+ keyword = GetString (ref i);
+ i++;
+ int len = data.Length - i;
+ text_data = new byte [len];
+ System.Array.Copy (data, i, text_data, 0, len);
+ }
+
+ public string Keyword {
+ get {
+ return keyword;
+ }
+ }
+
+ public byte [] TextData {
+ get {
+ return text_data;
+ }
+ }
+
+ public virtual void SetText (string text)
+ {
+ text_data = encoding.GetBytes (text);
+
+ byte [] keyword_data = Latin1.GetBytes (keyword);
+ data = new byte [keyword_data.Length + 1 + text_data.Length];
+ System.Array.Copy (keyword_data, 0, data, 0, keyword_data.Length);
+ data [keyword_data.Length] = 0;
+ System.Array.Copy (text_data, 0, data, keyword_data.Length + 1, text_data.Length);
+ }
+
+ public string Text {
+ get {
+ return encoding.GetString (text_data, 0, text_data.Length);
+ }
+ }
+ }
+
+ public class IccpChunk : Chunk {
+ string keyword;
+ byte [] profile;
+
+ public IccpChunk (string name, byte [] data) : base (name, data) {}
+
+ public override void Load (byte [] data)
+ {
+ int i = 0;
+ keyword = GetString (ref i);
+ i++;
+ int compression = data [i++];
+ if (compression != 0)
+ throw new System.Exception ("Unknown Compression type");
+
+ profile = Chunk.Inflate (data, i, data.Length - i);
+ }
+
+ public string Keyword {
+ get {
+ return keyword;
+ }
+ }
+
+ public byte [] Profile {
+ get {
+ return profile;
+ }
+ }
+ }
+
+ public class ItxtChunk : ZtxtChunk{
+ //public static string Name = "zTXt";
+
+ string Language;
+ string LocalizedKeyword;
+
+ public override void Load (byte [] data)
+ {
+ int i = 0;
+ keyword = GetString (ref i);
+ i++;
+ compressed = (data [i++] != 0);
+ Compression = data [i++];
+ Language = GetString (ref i);
+ i++;
+ LocalizedKeyword = GetString (ref i, System.Text.Encoding.UTF8);
+ i++;
+
+ if (Compressed) {
+ text_data = Chunk.Inflate (data, i, data.Length - i);
+ } else {
+ int len = data.Length - i;
+ text_data = new byte [len];
+ System.Array.Copy (data, i, text_data, 0, len);
+ }
+ }
+
+ public override void SetText (string text)
+ {
+ byte [] raw = System.Text.Encoding.UTF8.GetBytes (text);
+ SetText (raw);
+ }
+
+ public void SetText (byte [] raw)
+ {
+ using (MemoryStream stream = new MemoryStream ()) {
+ byte [] tmp;
+
+ text_data = raw;
+
+ tmp = Latin1.GetBytes (keyword);
+ stream.Write (tmp, 0, tmp.Length);
+ stream.WriteByte (0);
+
+ stream.WriteByte ((byte)(compressed ? 1 : 0));
+ stream.WriteByte (Compression);
+
+ if (Language != null && Language != System.String.Empty) {
+ tmp = Latin1.GetBytes (Language);
+ stream.Write (tmp, 0, tmp.Length);
+ }
+ stream.WriteByte (0);
+
+ if (LocalizedKeyword != null && LocalizedKeyword != System.String.Empty) {
+ tmp = System.Text.Encoding.UTF8.GetBytes (LocalizedKeyword);
+ stream.Write (tmp, 0, tmp.Length);
+ }
+ stream.WriteByte (0);
+
+ if (compressed) {
+ tmp = Deflate (text_data, 0, text_data.Length);
+ stream.Write (tmp, 0, tmp.Length);
+ } else {
+ stream.Write (text_data, 0, text_data.Length);
+ }
+ this.data = stream.ToArray ();
+ }
+ }
+
+ public ItxtChunk (string name, byte [] data) : base (name, data)
+ {
+ this.Name = name;
+ encoding = System.Text.Encoding.UTF8;
+ }
+
+ public ItxtChunk (string keyword, string language, bool compressed) : base ()
+ {
+ encoding = System.Text.Encoding.UTF8;
+ this.Name = "iTXt";
+ this.keyword = keyword;
+ this.Language = language;
+ this.LocalizedKeyword = System.String.Empty;
+ this.compressed = compressed;
+ this.Compression = 0;
+ }
+ }
+
+ public class TimeChunk : Chunk {
+ //public static string Name = "tIME";
+
+ System.DateTime time;
+
+ public System.DateTime Time {
+ get {
+ return new System.DateTime (FSpot.BitConverter.ToUInt16 (data, 0, false),
+ data [2], data [3], data [4], data [5], data [6]);
+
+ }
+ set {
+ byte [] year = BitConverter.GetBytes ((ushort)value.Year, false);
+ data [0] = year [0];
+ data [1] = year [1];
+ data [2] = (byte) value.Month;
+ data [3] = (byte) value.Day;
+ data [4] = (byte) value.Hour;
+ data [6] = (byte) value.Minute;
+ data [7] = (byte) value.Second;
+ }
+ }
+
+ public TimeChunk (string name, byte [] data) : base (name, data) {}
+
+ public TimeChunk ()
+ {
+ this.Name = "tIME";
+ this.Time = System.DateTime.Now;
+ }
+ }
+#if false
+ public class StandardRgbChunk : Chunk {
+ public StandardRgbChunk (string name, byte [] data) : base (name, data) {}
+
+ public Cms.Intent RenderingIntent {
+ get {
+ return (Cms.Intent) data [0];
+ }
+ }
+ }
+#endif
+ public class GammaChunk : Chunk {
+ public GammaChunk (string name, byte [] data) : base (name, data) {}
+ private const int divisor = 100000;
+
+ public double Gamma {
+ get {
+ return FSpot.BitConverter.ToUInt32 (data, 0, false) / (double) divisor;
+ }
+ }
+ }
+
+ public class ColorChunk : Chunk {
+ // FIXME this should be represented like a tiff rational
+ public const uint Denominator = 100000;
+
+ public ColorChunk (string name, byte [] data) : base (name, data) {}
+
+ public FSpot.Tiff.Rational WhiteX {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 0, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational WhiteY {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 4, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational RedX {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 8, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational RedY {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 12, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational GreenX {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 16, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational GreenY {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 20, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational BlueX {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 24, false), Denominator);
+ }
+ }
+ public FSpot.Tiff.Rational BlueY {
+ get {
+ return new FSpot.Tiff.Rational (FSpot.BitConverter.ToUInt32 (data, 28, false), Denominator);
+ }
+ }
+ }
+
+ public enum ColorType : byte {
+ Gray = 0,
+ Rgb = 2,
+ Indexed = 3,
+ GrayAlpha = 4,
+ RgbA = 6
+ };
+
+ public enum CompressionMethod : byte {
+ Zlib = 0
+ };
+
+ public enum InterlaceMethod : byte {
+ None = 0,
+ Adam7 = 1
+ };
+
+ public enum FilterMethod : byte {
+ Adaptive = 0
+ }
+
+ // Filter Types Show up as the first byte of each scanline
+ public enum FilterType {
+ None = 0,
+ Sub = 1,
+ Up = 2,
+ Average = 3,
+ Paeth = 4
+ };
+
+ public class IhdrChunk : Chunk {
+ public uint Width;
+ public uint Height;
+ public byte Depth;
+ public ColorType Color;
+ public PngFile.CompressionMethod Compression;
+ public FilterMethod Filter;
+ public InterlaceMethod Interlace;
+
+ public IhdrChunk (string name, byte [] data) : base (name, data) {}
+
+ public override void Load (byte [] data)
+ {
+ Width = BitConverter.ToUInt32 (data, 0, false);
+ Height = BitConverter.ToUInt32 (data, 4, false);
+ Depth = data [8];
+ Color = (ColorType) data [9];
+ //if (Color != ColorType.Rgb)
+ // throw new System.Exception (System.String.Format ("unsupported {0}", Color));
+
+ this.Compression = (CompressionMethod) data [10];
+ if (this.Compression != CompressionMethod.Zlib)
+ throw new System.Exception (System.String.Format ("unsupported {0}", Compression));
+
+ Filter = (FilterMethod) data [11];
+ if (Filter != FilterMethod.Adaptive)
+ throw new System.Exception (System.String.Format ("unsupported {0}", Filter));
+
+ Interlace = (InterlaceMethod) data [12];
+ //if (Interlace != InterlaceMethod.None)
+ // throw new System.Exception (System.String.Format ("unsupported {0}", Interlace));
+
+ }
+
+ public int ScanlineComponents {
+ get {
+ switch (Color) {
+ case ColorType.Gray:
+ case ColorType.Indexed:
+ return 1;
+ case ColorType.GrayAlpha:
+ return 2;
+ case ColorType.Rgb:
+ return 3;
+ case ColorType.RgbA:
+ return 4;
+ default:
+ throw new System.Exception (System.String.Format ("Unknown format {0}", Color));
+ }
+ }
+ }
+
+ public uint GetScanlineLength (int pass)
+ {
+ uint length = 0;
+ if (Interlace == InterlaceMethod.None) {
+ int bits = ScanlineComponents * Depth;
+ length = (uint) (this.Width * bits / 8);
+
+ // and a byte for the FilterType
+ length ++;
+ } else {
+ throw new System.Exception (System.String.Format ("unsupported {0}", Interlace));
+ }
+
+ return length;
+ }
+ }
+
+ public class Crc {
+ static uint [] lookup;
+ uint value = 0xffffffff;
+ uint length;
+ System.IO.Stream stream;
+
+ public uint Value {
+ get { return (value ^ 0xffffffff); }
+ }
+
+ public uint Length {
+ get { return length; }
+ }
+
+ static Crc () {
+ lookup = new uint [265];
+ uint c, n;
+ int k;
+
+ for (n = 0; n < 256; n++) {
+ c = n;
+ for (k = 0; k < 8; k++) {
+ if ((c & 1) != 0)
+ c = 0xedb88320 ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ lookup [n] = c;
+ }
+ }
+
+ public Crc ()
+ {
+ }
+
+ public Crc (System.IO.Stream stream)
+ {
+ this.stream = stream;
+ }
+
+ public void Write (byte [] buffer)
+ {
+ Write (buffer, 0, buffer.Length);
+ }
+
+ public void Write (byte [] buffer, int offset, int len)
+ {
+ for (int i = offset; i < len; i++)
+ value = lookup [(value ^ buffer[i]) & 0xff] ^ (value >> 8);
+
+ length += (uint)len;
+
+ if (stream != null)
+ stream.Write (buffer, offset, len);
+ }
+
+ public void WriteSum ()
+ {
+ byte [] data = BitConverter.GetBytes (Value, false);
+ stream.Write (data, 0, data.Length);
+ }
+ }
+
+ public class Chunk {
+ public string Name;
+ protected byte [] data;
+ protected static System.Collections.Hashtable name_table;
+
+ public byte [] Data {
+ get {
+ return data;
+ }
+ set {
+ Load (value);
+ }
+ }
+
+ static Chunk ()
+ {
+
+ name_table = new System.Collections.Hashtable ();
+ name_table ["iTXt"] = typeof (ItxtChunk);
+ name_table ["tXMP"] = typeof (ItxtChunk);
+ name_table ["tEXt"] = typeof (TextChunk);
+ name_table ["zTXt"] = typeof (ZtxtChunk);
+ name_table ["tIME"] = typeof (TimeChunk);
+ name_table ["iCCP"] = typeof (IccpChunk);
+ name_table ["IHDR"] = typeof (IhdrChunk);
+ name_table ["cHRM"] = typeof (ColorChunk);
+ name_table ["pHYs"] = typeof (PhysChunk);
+ name_table ["gAMA"] = typeof (GammaChunk);
+#if false
+ name_table ["sRGB"] = typeof (StandardRgbChunk);
+#endif
+ }
+
+ protected Chunk ()
+ {
+ }
+
+ public Chunk (string name, byte [] data)
+ {
+ this.Name = name;
+ this.data = data;
+ Load (data);
+ }
+
+ protected string GetString (ref int i, System.Text.Encoding enc)
+ {
+ for (; i < data.Length; i++) {
+ if (data [i] == 0)
+ break;
+ }
+
+ return enc.GetString (data, 0, i);
+ }
+
+ protected string GetString (ref int i)
+ {
+ return GetString (ref i, TextChunk.Latin1);
+ }
+
+ public virtual void Load (byte [] data)
+ {
+
+ }
+
+ public virtual void Save (System.IO.Stream stream)
+ {
+ byte [] name_bytes = System.Text.Encoding.ASCII.GetBytes (Name);
+ byte [] length_bytes = BitConverter.GetBytes ((uint)data.Length, false);
+ stream.Write (length_bytes, 0, length_bytes.Length);
+ Crc crc = new Crc (stream);
+ crc.Write (name_bytes);
+ crc.Write (data);
+ crc.WriteSum ();
+ }
+
+ public bool Critical {
+ get {
+ return !System.Char.IsLower (Name, 0);
+ }
+ }
+
+ public bool Private {
+ get {
+ return System.Char.IsLower (Name, 1);
+ }
+ }
+
+ public bool Reserved {
+ get {
+ return System.Char.IsLower (Name, 2);
+ }
+ }
+
+ public bool Safe {
+ get {
+ return System.Char.IsLower (Name, 3);
+ }
+ }
+
+ public bool CheckCrc (uint value)
+ {
+ byte [] name = System.Text.Encoding.ASCII.GetBytes (Name);
+ Crc crc = new Crc ();
+ crc.Write (name);
+ crc.Write (data);
+
+ return crc.Value == value;
+ }
+
+ public static Chunk Generate (string name, byte [] data)
+ {
+ System.Type t = (System.Type) name_table [name];
+
+ Chunk chunk;
+ if (t != null)
+ chunk = (Chunk) System.Activator.CreateInstance (t, new object[] {name, data});
+ else
+ chunk = new Chunk (name, data);
+
+ return chunk;
+ }
+
+ public static byte [] Inflate (byte [] input, int start, int length)
+ {
+ System.IO.MemoryStream output = new System.IO.MemoryStream ();
+ Inflater inflater = new Inflater ();
+
+ inflater.SetInput (input, start, length);
+
+ byte [] buf = new byte [1024];
+ int inflate_length;
+ while ((inflate_length = inflater.Inflate (buf)) > 0)
+ output.Write (buf, 0, inflate_length);
+
+ output.Close ();
+ return output.ToArray ();
+ }
+
+ public static byte [] Deflate (byte [] input, int offset, int length)
+ {
+ System.IO.MemoryStream output = new System.IO.MemoryStream ();
+ Deflater deflater = new Deflater ();
+ deflater.SetInput (input, offset, length);
+
+ byte [] buf = new byte [1024];
+ int deflate_length;
+ while ((deflate_length = deflater.Deflate (buf)) > 0)
+ output.Write (buf, 0, deflate_length);
+
+ output.Close ();
+ return output.ToArray ();
+ }
+ }
+
+ public class ChunkInflater {
+ private Inflater inflater;
+ private System.Collections.ArrayList chunks;
+
+ public ChunkInflater ()
+ {
+ inflater = new Inflater ();
+ chunks = new System.Collections.ArrayList ();
+ }
+
+ public bool Fill ()
+ {
+ while (inflater.IsNeedingInput && chunks.Count > 0) {
+ inflater.SetInput (((Chunk)chunks[0]).Data);
+ //System.Console.WriteLine ("adding chunk {0}", ((Chunk)chunks[0]).Data.Length);
+ chunks.RemoveAt (0);
+ }
+ return true;
+ }
+
+ public int Inflate (byte [] data, int start, int length)
+ {
+ int result = 0;
+ do {
+ Fill ();
+ result += inflater.Inflate (data, start + result, length - result);
+ //System.Console.WriteLine ("Attempting Second after fill Inflate {0} {1} {2}", attempt, result, length - result);
+ } while (result < length && chunks.Count > 0);
+
+ return result;
+ }
+
+ public void Add (Chunk chunk)
+ {
+ chunks.Add (chunk);
+ }
+ }
+
+#if false
+ public class ScanlineDecoder {
+ int width;
+ int height;
+ int row;
+ int col;
+ ChunkInflater inflater;
+ byte [] buffer;
+
+ public ScanlineDecoder (ChunkInflater inflater, uint width, uint height)
+ {
+ this.inflater = inflater;
+ this.row = 0;
+ this.height = (int)height;
+ this.width = (int)width;
+
+ buffer = new byte [width * height];
+
+ Fill ();
+ }
+
+ public void Fill ()
+ {
+ for (; row < height; row ++) {
+ col = inflater.Inflate (buffer, row * width, width);
+
+ if (col < width) {
+ inflater.Fill ();
+ System.Console.WriteLine ("short read missing {0} {1} {2}", width - col, row, height);
+ }
+ }
+ }
+
+ private static byte PaethPredict (byte a, byte b, byte c)
+ {
+ int p = a + b - c;
+ int pa = System.Math.Abs (p - a);
+ int pb = System.Math.Abs (p - b);
+ int pc = System.Math.Abs (p - c);
+ if (pa <= pb && pa <= pc)
+ return a;
+ else if (pb <= pc)
+ return b;
+ else
+ return c;
+ }
+
+ public void ReconstructRow (int row, int channels)
+ {
+ int offset = row * width;
+ FilterType type = (FilterType) buffer [offset];
+ byte a = 0;
+ byte x;
+ byte b;
+ byte c = 0;
+
+ offset++;
+ //buffer [offset++] = 0;
+
+ int prev_line;
+
+ //System.Console.WriteLine ("type = {0}", type);
+ for (int col = 1; col < this.width; col++) {
+ x = buffer [offset];
+
+ prev_line = offset - width;
+
+ a = col <= channels ? (byte) 0 : (byte) buffer [offset - channels];
+ b = (prev_line) < 0 ? (byte) 0 : (byte) buffer [prev_line];
+ c = (prev_line) < 0 || (col <= channels) ? (byte) 0 : (byte) buffer [prev_line - channels];
+
+#if false
+ switch (type) {
+ case FilterType.None:
+ break;
+ case FilterType.Sub:
+ x = (byte) (x + a);
+ break;
+ case FilterType.Up:
+ x = (byte) (x + b);
+ break;
+ case FilterType.Average:
+ x = (byte) (x + ((a + b) >> 1));
+ break;
+ case FilterType.Paeth:
+ x = (byte) (x + PaethPredict (a, b, c));
+ break;
+ default:
+ throw new System.Exception (System.String.Format ("Invalid FilterType {0}", type));
+ }
+#else
+ if (type == FilterType.Sub) {
+ x = (byte) (x + a);
+ } else if (type == FilterType.Up) {
+ x = (byte) (x + b);
+ } else if (type == FilterType.Average) {
+ x = (byte) (x + ((a + b) >> 1));
+ } else if (type == FilterType.Paeth) {
+ int p = a + b - c;
+ int pa = System.Math.Abs (p - a);
+ int pb = System.Math.Abs (p - b);
+ int pc = System.Math.Abs (p - c);
+ if (pa <= pb && pa <= pc)
+ x = (byte)(x + a);
+ else if (pb <= pc)
+ x = (byte)(x + b);
+ else
+ x = (byte)(x + c);
+ }
+#endif
+ //System.Console.Write ("{0}.", x);
+ buffer [offset ++] = x;
+ }
+
+ }
+
+ public unsafe void UnpackRGBIndexedLine (Gdk.Pixbuf dest, int line, int depth, byte [] palette, byte [] alpha)
+ {
+ int pos = line * width + 1;
+ byte * pixels = (byte *) dest.Pixels;
+
+ pixels += line * dest.Rowstride;
+ int channels = dest.NChannels;
+ int div = (8 / depth);
+ byte mask = (byte)(0xff >> (8 - depth));
+
+ for (int i = 0; i < dest.Width; i++) {
+ int val = buffer [pos + i / div];
+ int shift = (8 - depth) - (i % div) * depth;
+
+ val = (byte) ((val & (byte)(mask << shift)) >> shift);
+
+ pixels [i * channels] = palette [val * 3];
+ pixels [i * channels + 1] = palette [val * 3 + 1];
+ pixels [i * channels + 2] = palette [val * 3 + 2];
+
+ if (channels > 3 && alpha != null)
+ pixels [i * channels + 3] = val < alpha.Length ? alpha [val] : (byte)0xff;
+ }
+ }
+
+ public unsafe void UnpackRGB16Line (Gdk.Pixbuf dest, int line, int channels)
+ {
+ int pos = line * width + 1;
+ byte * pixels = (byte *) dest.Pixels;
+
+ pixels += line * dest.Rowstride;
+
+ if (dest.NChannels != channels)
+ throw new System.Exception ("bad pixbuf format");
+
+ int i = 0;
+ int length = dest.Width * channels;
+ while (i < length) {
+ pixels [i++] = (byte) (BitConverter.ToUInt16 (buffer, pos, false) >> 8);
+ pos += 2;
+ }
+
+ }
+
+ public unsafe void UnpackRGB8Line (Gdk.Pixbuf dest, int line, int channels)
+ {
+ int pos = line * width + 1;
+ byte * pixels = (byte *) dest.Pixels;
+
+ pixels += line * dest.Rowstride;
+ if (dest.NChannels != channels)
+ throw new System.Exception ("bad pixbuf format");
+
+ System.Runtime.InteropServices.Marshal.Copy (buffer, pos,
+ (System.IntPtr)pixels, dest.Width * channels);
+
+ }
+
+ public unsafe void UnpackGrayLine (Gdk.Pixbuf dest, int line, int depth, bool alpha)
+ {
+ int pos = line * width + 1;
+ byte * pixels = (byte *) dest.Pixels;
+
+ pixels += line * dest.Rowstride;
+ int div = (8 / depth);
+ byte mask = (byte)(0xff >> (8 - depth));
+ int length = dest.Width * (alpha ? 2 : 1);
+
+ for (int i = 0; i < length; i++) {
+ byte val = buffer [pos + i / div];
+ int shift = (8 - depth) - (i % div) * depth;
+
+ if (depth != 8) {
+ val = (byte) ((val & (byte)(mask << shift)) >> shift);
+ val = (byte) (((val * 0xff) + (mask >> 1)) / mask);
+ }
+
+ if (!alpha || i % 2 == 0) {
+ pixels [0] = val;
+ pixels [1] = val;
+ pixels [2] = val;
+ pixels += 3;
+ } else {
+ pixels [0] = val;
+ pixels ++;
+ }
+ }
+ }
+
+ public unsafe void UnpackGray16Line (Gdk.Pixbuf dest, int line, bool alpha)
+ {
+ int pos = line * width + 1;
+ byte * pixels = (byte *) dest.Pixels;
+
+ pixels += line * dest.Rowstride;
+
+ int i = 0;
+ while (i < dest.Width) {
+ byte val = (byte) (BitConverter.ToUInt16 (buffer, pos, false) >> 8);
+ pixels [0] = val;
+ pixels [1] = val;
+ pixels [2] = val;
+ if (alpha) {
+ pos += 2;
+ pixels [3] = (byte)(BitConverter.ToUInt16 (buffer, pos, false) >> 8);
+ }
+ pos += 2;
+ pixels += dest.NChannels;
+ i++;
+ }
+ }
+
+
+ }
+
+ public Gdk.Pixbuf GetPixbuf ()
+ {
+ ChunkInflater ci = new ChunkInflater ();
+ Chunk palette = null;
+ Chunk transparent = null;
+
+ foreach (Chunk chunk in Chunks) {
+ if (chunk.Name == "IDAT")
+ ci.Add (chunk);
+ else if (chunk.Name == "PLTE")
+ palette = chunk;
+ else if (chunk.Name == "tRNS")
+ transparent = chunk;
+ }
+
+ IhdrChunk ihdr = (IhdrChunk) Chunks [0];
+ System.Console.WriteLine ("Attempting to to inflate photo {0}.{1}({2}, {3})", ihdr.Color, ihdr.Depth, ihdr.Width, ihdr.Height);
+ ScanlineDecoder decoder = new ScanlineDecoder (ci, ihdr.GetScanlineLength (0), ihdr.Height);
+ decoder.Fill ();
+ //Gdk.Pixbuf pixbuf = decoder.GetPixbuf ();
+
+ //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXXXXXXXX Inflate ############################");
+
+ bool alpha = (ihdr.Color == ColorType.GrayAlpha || ihdr.Color == ColorType.RgbA || transparent != null);
+
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (Gdk.Colorspace.Rgb,
+ alpha, 8, (int)ihdr.Width, (int)ihdr.Height);
+
+ for (int line = 0; line < ihdr.Height; line++) {
+ switch (ihdr.Color) {
+ case ColorType.Rgb:
+ if (ihdr.Depth == 16) {
+ decoder.ReconstructRow (line, 6);
+ decoder.UnpackRGB16Line (pixbuf, line, 3);
+ } else {
+ decoder.ReconstructRow (line, 3);
+ decoder.UnpackRGB8Line (pixbuf, line, 3);
+ }
+ break;
+ case ColorType.RgbA:
+ if (ihdr.Depth == 16) {
+ decoder.ReconstructRow (line, 8);
+ decoder.UnpackRGB16Line (pixbuf, line, 4);
+ } else {
+ decoder.ReconstructRow (line, 4);
+ decoder.UnpackRGB8Line (pixbuf, line, 4);
+ }
+ break;
+ case ColorType.GrayAlpha:
+ switch (ihdr.Depth) {
+ case 16:
+ decoder.ReconstructRow (line, 4);
+ decoder.UnpackGray16Line (pixbuf, line, true);
+ break;
+ default:
+ decoder.ReconstructRow (line, 2);
+ decoder.UnpackGrayLine (pixbuf, line, ihdr.Depth, true);
+ break;
+ }
+ break;
+ case ColorType.Gray:
+ switch (ihdr.Depth) {
+ case 16:
+ decoder.ReconstructRow (line, 2);
+ decoder.UnpackGray16Line (pixbuf, line, false);
+ break;
+ default:
+ decoder.ReconstructRow (line, 1);
+ decoder.UnpackGrayLine (pixbuf, line, ihdr.Depth, false);
+ break;
+ }
+ break;
+ case ColorType.Indexed:
+ decoder.ReconstructRow (line, 1);
+ decoder.UnpackRGBIndexedLine (pixbuf,
+ line,
+ ihdr.Depth,
+ palette.Data,
+ transparent != null ? transparent.Data : null);
+ break;
+ default:
+ throw new System.Exception (System.String.Format ("unhandled color type {0}", ihdr.Color));
+ }
+ }
+ return pixbuf;
+ }
+#endif
+ private static byte [] magic = new byte [] { 137, 80, 78, 71, 13, 10, 26, 10 };
+
+
+ public class PngHeader {
+ ArrayList chunk_list;
+
+ public ArrayList Chunks {
+ get { return chunk_list; }
+ }
+
+ public PngHeader (Stream stream)
+ {
+ byte [] heading = new byte [8];
+ byte [] crc_data = new byte [4];
+ stream.Read (heading, 0, heading.Length);
+
+ for (int i = 0; i < heading.Length; i++)
+ if (heading [i] != magic [i])
+ throw new System.Exception ("Invalid PNG magic number");
+
+ chunk_list = new System.Collections.ArrayList ();
+
+ for (int i = 0; stream.Read (heading, 0, heading.Length) == heading.Length; i++) {
+ uint length = BitConverter.ToUInt32 (heading, 0, false);
+ string name = System.Text.Encoding.ASCII.GetString (heading, 4, 4);
+ byte [] data = new byte [length];
+ if (length > 0)
+ stream.Read (data, 0, data.Length);
+
+ stream.Read (crc_data, 0, 4);
+ uint crc = BitConverter.ToUInt32 (crc_data, 0, false);
+
+ Chunk chunk = Chunk.Generate (name, data);
+ if (! chunk.CheckCrc (crc))
+ throw new System.Exception ("chunk crc check failed");
+
+ //System.Console.Write ("read one {0} {1}", chunk, chunk.Name);
+ chunk_list.Add (chunk);
+
+#if false
+ if (chunk is TextChunk) {
+ TextChunk text = (TextChunk) chunk;
+ System.Console.Write (" Text Chunk {0} {1}",
+ text.Keyword, System.String.Empty, System.String.Empty);
+ }
+
+ TimeChunk time = chunk as TimeChunk;
+ if (time != null)
+ System.Console.Write(" Time {0}", time.Time);
+
+ System.Console.WriteLine (System.String.Empty);
+#endif
+
+ if (chunk.Name == "IEND")
+ break;
+ }
+ }
+
+ internal string LookupText (string keyword)
+ {
+ TextChunk chunk = LookupTextChunk (keyword);
+ if (chunk != null)
+ return chunk.Text;
+
+ return null;
+ }
+
+ internal TextChunk LookupTextChunk (string keyword)
+ {
+ foreach (Chunk chunk in Chunks) {
+ TextChunk text = chunk as TextChunk;
+ if (text != null && text.Keyword == keyword)
+ return text;
+ }
+ return null;
+ }
+
+ internal void Insert (Chunk chunk)
+ {
+ // FIXME The point of this function is to enforce ordering constraints
+ // it obviously isn't complete right now.
+ if (chunk is IhdrChunk)
+ Chunks.Insert (0, chunk);
+ else if (chunk is TextChunk)
+ Chunks.Insert (1, chunk);
+ else
+ throw new System.Exception ("Uknown ordering for chunk");
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ stream.Write (magic, 0, magic.Length);
+ foreach (Chunk chunk in Chunks) {
+ chunk.Save (stream);
+ }
+ }
+ }
+
+
+ public void Save (System.IO.Stream stream)
+ {
+ Header.Save (stream);
+ }
+
+#if false
+ public void Save (string path)
+ {
+ string temp_path = path + ".tmp.png";
+ using (System.IO.Stream output = System.IO.File.OpenWrite (temp_path)) {
+ Save (output);
+ }
+ if (FSpot.Utils.Unix.Rename (temp_path, path) < 0) {
+ System.IO.File.Delete (temp_path);
+ throw new System.Exception (System.String.Format ("Unable to rename {0} to {1}", temp_path, path));
+ }
+ }
+
+ public override void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+ byte [] buffer = PixbufUtils.Save (pixbuf, "png", null, null);
+ using (MemoryStream mem = new MemoryStream (buffer)) {
+ PngHeader converted = new PngHeader (mem);
+
+ /* FIXME we need to update the XMP metadata here */
+ foreach (Chunk c in Chunks) {
+ if (c is TextChunk) {
+ converted.Insert (c);
+ }
+ }
+
+ converted.Save (stream);
+ }
+ }
+
+ public override Cms.Profile GetProfile ()
+ {
+ ColorChunk color = null;
+ IccpChunk icc = null;
+ GammaChunk gamma = null;
+ StandardRgbChunk srgb = null;
+ double gamma_value = 2.2;
+ ColorCIExyY red = new ColorCIExyY (0.64, 0.33, 1.0);
+ ColorCIExyY green = new ColorCIExyY (0.3, 0.6, 1.0);
+ ColorCIExyY blue = new ColorCIExyY (0.15, 0.06, 1.0);
+ ColorCIExyY whitepoint = new ColorCIExyY (0.3127, 0.329, 1.0);
+ ColorCIExyYTriple chroma = new ColorCIExyYTriple (red, green, blue);
+
+ //System.Console.WriteLine ("Trying to get profile");
+
+ foreach (Chunk chunk in Chunks) {
+ if (color == null)
+ color = chunk as ColorChunk;
+ if (icc == null)
+ icc = chunk as IccpChunk;
+ if (srgb == null)
+ srgb = chunk as StandardRgbChunk;
+ if (gamma == null)
+ gamma = chunk as GammaChunk;
+ }
+
+ //System.Console.WriteLine ("color: {0} icc: {1} srgb: {2} gamma: {3}", color, icc, srgb, gamma);
+
+ if (icc != null) {
+ try {
+ return new Profile (icc.Profile);
+ } catch (System.Exception ex) {
+ System.Console.WriteLine ("Error trying to decode embedded profile" + ex.ToString ());
+ }
+ }
+
+ if (srgb != null)
+ return Profile.CreateStandardRgb ();
+
+ if (gamma != null)
+ gamma_value = 1 / gamma.Gamma;
+
+ if (color != null) {
+ whitepoint = new ColorCIExyY (color.WhiteX.Value, color.WhiteY.Value, 1.0);
+ red = new ColorCIExyY (color.RedX.Value, color.RedY.Value, 1.0);
+ green = new ColorCIExyY (color.GreenX.Value, color.GreenY.Value, 1.0);
+ blue = new ColorCIExyY (color.BlueX.Value, color.BlueY.Value, 1.0);
+ chroma = new ColorCIExyYTriple (red, green, blue);
+ }
+
+ if (color != null || gamma != null) {
+ GammaTable table = new GammaTable (1024, gamma_value);
+ return new Profile (whitepoint, chroma, new GammaTable [] {table, table, table});
+ }
+
+ return null;
+ }
+#endif
+ public override string Description {
+ get {
+ string description = Header.LookupText ("Description");
+
+ if (description != null)
+ return description;
+ else
+ return Header.LookupText ("Comment");
+ }
+ }
+
+ public void SetDescription (string description)
+ {
+ TextChunk text = null;
+ text = Header.LookupTextChunk ("Description");
+
+ if (text != null)
+ text.SetText (description);
+ else
+ Header.Insert (new TextChunk ("Description", description));
+ }
+
+ public XmpFile GetXmp ()
+ {
+ TextChunk xmpchunk = Header.LookupTextChunk ("XML:com.adobe.xmp");
+ if (xmpchunk == null)
+ xmpchunk = Header.LookupTextChunk ("XMP");
+
+ if (xmpchunk == null)
+ return null;
+
+ using (MemoryStream stream = new MemoryStream (xmpchunk.TextData)) {
+ return new XmpFile (stream);
+ }
+ }
+
+ public void SetXmp (XmpFile xmp)
+ {
+ TextChunk text = null;
+
+ text = Header.LookupTextChunk ("XML:com.adobe.xmp");
+ if (text != null)
+ Chunks.Remove (text);
+
+ text = Header.LookupTextChunk ("XMP");
+ if (text != null)
+ Chunks.Remove (text);
+
+ ItxtChunk itext = new ItxtChunk ("XML:com.adobe.xmp", "en", false);
+ using (MemoryStream stream = new MemoryStream ()) {
+ xmp.Save (stream);
+ itext.SetText (stream.ToArray ());
+ }
+ Header.Insert (itext);
+ }
+
+ public override System.DateTime Date {
+ get {
+ // FIXME: we should first try parsing the
+ // LookupText ("Creation Time") as a valid date
+
+ foreach (Chunk chunk in Chunks) {
+ TimeChunk time = chunk as TimeChunk;
+ if (time != null)
+ return time.Time.ToUniversalTime ();
+ }
+ return base.Date;
+ }
+ }
+
+#if ENABLE_NUNIT
+ [TestFixture]
+ public class Tests {
+ public Tests ()
+ {
+ Gnome.Vfs.Vfs.Initialize ();
+ Gtk.Application.Init ();
+ }
+
+ [Test]
+ public void Save ()
+ {
+ Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
+ string path = ImageFile.TempPath ("joe.png");
+ test.Save (path, "png");
+ PngFile pimg = new PngFile (path);
+
+ string desc = "this is a png test";
+ string desc2 = "\000xa9 Novell Inc.";
+ pimg.SetDescription (desc);
+ using (Stream stream = File.OpenWrite (path)) {
+ pimg.Save (stream);
+ }
+ PngFile mod = new PngFile (path);
+ Assert.AreEqual (mod.Orientation, PixbufOrientation.TopLeft);
+ Assert.AreEqual (mod.Description, desc);
+ pimg.SetDescription (desc2);
+
+ using (Stream stream = File.OpenWrite (path)) {
+ pimg.Save (stream);
+ }
+ mod = new PngFile (path);
+ Assert.AreEqual (mod.Description, desc2);
+
+ File.Delete (path);
+ }
+
+ [Test]
+ public void Load ()
+ {
+ string desc = "(c) 2004 Jakub Steiner\n\nCreated with The GIMP";
+ Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly ();
+ string path = ImageFile.TempPath ("maddy.png");
+ using (Stream output = File.OpenWrite (path)) {
+ using (Stream source = assembly.GetManifestResourceStream ("f-spot-adjust-colors.png")) {
+ byte [] buffer = new byte [256];
+ while (source.Read (buffer, 0, buffer.Length) > 0) {
+ output.Write (buffer, 0, buffer.Length);
+ }
+ }
+ }
+ PngFile pimg = new PngFile (path);
+ Assert.AreEqual (pimg.Description, desc);
+
+ File.Delete (path);
+ }
+ }
+#endif
+
+#if false
+ public class ImageFile {
+ string Path;
+ public ImageFile (string path)
+ {
+ this.Path = path;
+ }
+ }
+
+ public static void Main (string [] args)
+ {
+ System.Collections.ArrayList failed = new System.Collections.ArrayList ();
+ Gtk.Application.Init ();
+ foreach (string path in args) {
+ Gtk.Window win = new Gtk.Window (path);
+ Gtk.HBox box = new Gtk.HBox ();
+ box.Spacing = 12;
+ win.Add (box);
+ Gtk.Image image;
+ image = new Gtk.Image ();
+
+ System.DateTime start = System.DateTime.Now;
+ System.TimeSpan one = start - start;
+ System.TimeSpan two = start - start;
+ try {
+ start = System.DateTime.Now;
+ image.Pixbuf = new Gdk.Pixbuf (path);
+ one = System.DateTime.Now - start;
+ } catch (System.Exception e) {
+ }
+ box.PackStart (image);
+
+ image = new Gtk.Image ();
+ try {
+ start = System.DateTime.Now;
+ PngFile png = new PngFile (path);
+ image.Pixbuf = png.GetPixbuf ();
+ two = System.DateTime.Now - start;
+ } catch (System.Exception e) {
+ failed.Add (path);
+ //System.Console.WriteLine ("Error loading {0}", path);
+ System.Console.WriteLine (e.ToString ());
+ }
+
+ System.Console.WriteLine ("{2} Load Time {0} vs {1}", one.TotalMilliseconds, two.TotalMilliseconds, path);
+ box.PackStart (image);
+ win.ShowAll ();
+ }
+
+ System.Console.WriteLine ("{0} Failed to Load", failed.Count);
+ foreach (string fail_path in failed) {
+ System.Console.WriteLine (fail_path);
+ }
+
+ Gtk.Application.Run ();
+ }
+#endif
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/PnmFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/PnmFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,319 @@
+using SemWeb;
+using System;
+using System.IO;
+
+#if ENABLE_NUNIT
+using NUnit.Framework;
+#endif
+
+namespace FSpot.Pnm {
+ public class PnmFile : ImageFile, StatementSource {
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public PnmFile (Uri uri) : base (uri)
+ {
+ }
+
+ public PnmFile (string path) : base (path)
+ {
+ }
+
+ public class Header {
+ public string Magic;
+ public int Width;
+ public int Height;
+ public ushort Max;
+
+ public Header (Stream stream)
+ {
+ Magic = GetString (stream);
+ Width = int.Parse (GetString (stream));
+ Height = int.Parse (GetString (stream));
+ Max = ushort.Parse (GetString (stream));
+ }
+
+ public bool IsDeep {
+ get {
+ return Max > 256;
+ }
+ }
+
+ public void Dump ()
+ {
+ System.Console.WriteLine ("Loading ({0} - {1},{2} - {3})",
+ Magic, Width, Height, Max);
+ }
+ }
+
+ public void Select (StatementSink sink)
+ {
+ using (Stream stream = Open ()) {
+ Header header = new Header (stream);
+ MetadataStore.AddLiteral (sink, "tiff:ImageWidth", header.Width.ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageLength", header.Height.ToString ());
+ string bits = header.IsDeep ? "16" : "8";
+ MetadataStore.Add (sink, "tiff:BitsPerSample", "rdf:Seq", new string [] { bits, bits, bits });
+ }
+ }
+
+ public override Stream PixbufStream ()
+ {
+ Stream stream = Open ();
+ Header header = new Header (stream);
+ if (header.IsDeep)
+ return null;
+
+ stream.Position = 0;
+ return stream;
+ }
+
+ static char EatComment (Stream stream)
+ {
+ char c;
+ do {
+ c = (char)stream.ReadByte ();
+
+ } while (c != '\n' && c != '\n');
+
+ return c;
+ }
+
+ static string GetString (Stream stream)
+ {
+ System.Text.StringBuilder builder = new System.Text.StringBuilder ();
+
+ char c;
+ do {
+ c = (char)stream.ReadByte ();
+ if (c == '#')
+ c = EatComment (stream);
+
+ } while (char.IsWhiteSpace (c));
+
+ while (! char.IsWhiteSpace (c)) {
+ builder.Append (c);
+ c = (char)stream.ReadByte ();
+ }
+
+ return builder.ToString ();
+ }
+
+ public static ushort [] ReadShort (Stream stream, int width, int height, int channels)
+ {
+ int length = width * height * channels;
+ ushort [] data = new ushort [length];
+ byte [] tmp = new byte [2];
+
+ for (int i = 0; i < length; i++)
+ {
+ stream.Read (tmp, 0, tmp.Length);
+ data [i] = BitConverter.ToUInt16 (tmp, 0, false);
+ }
+ return data;
+ }
+
+#if false
+ static Gdk.Pixbuf LoadRGB16 (Stream stream, int width, int height)
+ {
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, 8, width, height);
+ unsafe {
+ byte *pixels = (byte *)pixbuf.Pixels;
+ int length = width * 6;
+ byte [] buffer = new byte [length];
+
+ for (int row = 0; row < height; row++) {
+ stream.Read (buffer, 0, buffer.Length);
+ for (int i = 0; i < width * 3; i++) {
+ pixels [i] = (byte) (BitConverter.ToUInt16 (buffer, i * 2, false) >> 8);
+ }
+ pixels += pixbuf.Rowstride;
+ }
+ }
+ return pixbuf;
+ }
+
+ static Gdk.Pixbuf LoadRGB8 (Stream stream, int width, int height)
+ {
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, 8, width, height);
+ unsafe {
+ byte *pixels = (byte *)pixbuf.Pixels;
+ byte [] buffer = new byte [width * 3];
+
+ for (int i = 0; i < height; i++) {
+ stream.Read (buffer, 0, buffer.Length);
+
+ System.Runtime.InteropServices.Marshal.Copy (buffer, 0,
+ (System.IntPtr)pixels, buffer.Length);
+
+ pixels += pixbuf.Rowstride;
+ }
+ }
+ return pixbuf;
+ }
+
+ static PixelBuffer LoadBufferRGB16 (Stream stream, int width, int height)
+ {
+ PixelBuffer pix = new UInt16Buffer (width, height);
+ int count = width * 3;
+ byte [] buffer = new byte [count * 2];
+
+ for (int row = 0; row < height; row++) {
+ int len = 0;
+ while (len < buffer.Length) {
+ int read = stream.Read (buffer, len, buffer.Length - len);
+ if (read < 0)
+ break;
+ len += read;
+ }
+
+ pix.Fill16 (row, 0, buffer, 0, count, false);
+ }
+
+ return pix;
+ }
+
+ static PixelBuffer LoadBufferRGB8 (Stream stream, int width, int height)
+ {
+ PixelBuffer pix = new UInt16Buffer (width, height);
+ int length = width * 3;
+ byte [] buffer = new byte [length];
+
+ for (int row = 0; row < height; row++) {
+ stream.Read (buffer, 0, buffer.Length);
+ pix.Fill8 (row, 0, buffer, 0, buffer.Length);
+ }
+
+ return pix;
+ }
+
+ public static FSpot.Imaging.PixelBuffer LoadBuffer (Stream stream)
+ {
+
+ Header header = new Header (stream);
+ header.Dump ();
+
+ switch (header.Magic) {
+ case "P6":
+ if (header.IsDeep)
+ return LoadBufferRGB16 (stream, header.Width, header.Height);
+ else
+ return LoadBufferRGB8 (stream, header.Width, header.Height);
+ default:
+ throw new System.Exception (System.String.Format ("unknown pnm type {0}", header.Magic));
+ }
+ }
+
+ public override Gdk.Pixbuf Load ()
+ {
+ try {
+ using (Stream stream = Open ()) {
+ Gdk.Pixbuf pixbuf = PnmFile.Load (stream);
+ return pixbuf;
+ }
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ return null;
+ }
+
+ public override Gdk.Pixbuf Load (int width, int height)
+ {
+ return PixbufUtils.ScaleToMaxSize (this.Load (), width, height);
+ }
+
+ public override void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+ if (pixbuf.HasAlpha)
+ throw new NotImplementedException ();
+
+ // FIXME this should be part of the header class
+ string header = String.Format ("P6\n"
+ + "#Software: {0} {1}\n"
+ + "{2} {3} #Width and Height\n"
+ + "255\n",
+ FSpot.Defines.PACKAGE,
+ FSpot.Defines.VERSION,
+ pixbuf.Width,
+ pixbuf.Height);
+
+ byte [] header_bytes = System.Text.Encoding.UTF8.GetBytes (header);
+ stream.Write (header_bytes, 0, header.Length);
+
+ unsafe {
+ byte * src_pixels = (byte *) pixbuf.Pixels;
+ int src_stride = pixbuf.Rowstride;
+ int count = pixbuf.Width * pixbuf.NChannels;
+ int height = pixbuf.Height;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < count; x++) {
+ stream.WriteByte (* (src_pixels + x));
+ }
+ src_pixels += src_stride;
+ }
+ }
+ }
+
+ public static Gdk.Pixbuf Load (Stream stream)
+ {
+ Header header = new Header (stream);
+ header.Dump ();
+
+ switch (header.Magic) {
+ case "P6":
+ if (header.IsDeep) {
+#if SKIP_BUFFER
+ return LoadRGB16 (stream, header.Width, header.Height);
+#else
+ stream.Position = 0;
+ FSpot.Imaging.PixelBuffer image = FSpot.Pnm.PnmFile.LoadBuffer (stream);
+ Gdk.Pixbuf result = image.ToPixbuf (Cms.Profile.CreateStandardRgb ());
+ return result;
+#endif
+ } else
+ return LoadRGB8 (stream, header.Width, header.Height);
+ default:
+ throw new System.Exception (System.String.Format ("unknown pnm type {0}", header.Magic));
+ }
+ }
+#endif
+ }
+
+#if ENABLE_NUNIT
+ [TestFixture]
+ public class Tests {
+ [Test]
+ public void SaveLoad ()
+ {
+ using (Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (null, "f-spot-32.png")) {
+ Gdk.Pixbuf source = pixbuf;
+ if (pixbuf.HasAlpha)
+ source = PixbufUtils.Flatten (pixbuf);
+
+ string path = ImageFile.TempPath ("test.ppm");
+ PnmFile pnm = new PnmFile (path);
+ using (Stream stream = File.OpenWrite (path)) {
+ pnm.Save (source, stream);
+ }
+
+ pnm = new PnmFile (path);
+
+ using (Gdk.Pixbuf saved = pnm.Load ()) {
+ Assert.IsNotNull (saved);
+ Assert.AreEqual (saved.Width, source.Width);
+ Assert.AreEqual (saved.Height, source.Height);
+ }
+
+ if (source != pixbuf)
+ source.Dispose ();
+
+ File.Delete (path);
+ }
+ }
+ }
+#endif
+}
Added: trunk/beagle/Util/F-Spot/Imaging/RafFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/RafFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,122 @@
+namespace FSpot.Raf {
+ // This is reverse engineered from looking at the sample files I have
+ // from what I can tell the file is always BigEndian, although the embedded jpeg may not be
+ // and there is a start long offset at 0x54 (or possibly 0x56 if it is a short) that points to
+ // the start of the embedded jpeg and followed by a long length that gives the length of the jpeg
+ // data.
+ //
+ // Following that there seem to be more offsets and lengths (probably for the raw data) that I haven't
+ // completely figured out yet. More to follow.
+
+ // ALL the sample files I have begin with "FUJIFILMCCD-RAW "
+
+
+ public class WhiteBalance {
+ // see dcraw parse_fuli
+ public WhiteBalance (System.IO.Stream stream)
+ {
+
+ }
+ }
+
+ public class RafFile : ImageFile, SemWeb.StatementSource {
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public RafFile (System.Uri uri) : base (uri)
+ {
+ }
+
+ private Exif.ExifData exif_data;
+ public Exif.ExifData ExifData {
+ get {
+ if (exif_data == null)
+ exif_data = new Exif.ExifData(uri.LocalPath);
+ System.Console.WriteLine ("loading exif data");
+ return exif_data;
+ }
+ }
+
+ public override PixbufOrientation GetOrientation (){
+ PixbufOrientation orientation = PixbufOrientation.TopLeft;
+
+ Exif.ExifEntry e = this.ExifData.GetContents (Exif.Ifd.Zero).Lookup (Exif.Tag.Orientation);
+ if (e != null) {
+ ushort [] value = e.GetDataUShort ();
+ orientation = (PixbufOrientation) value [0];
+ }
+
+ if (orientation < PixbufOrientation.TopLeft || orientation > PixbufOrientation.LeftBottom)
+ orientation = PixbufOrientation.TopLeft;
+
+ return orientation;
+ }
+
+ public RafFile (string path) : base (path)
+ {
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ byte [] data = GetEmbeddedJpeg ();
+
+ if (data != null)
+ return new System.IO.MemoryStream (data);
+ else
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+#if false
+ public override Gdk.Pixbuf Load ()
+ {
+ return new Gdk.Pixbuf (PixbufStream ());
+ }
+
+ public override Gdk.Pixbuf Load (int width, int height)
+ {
+ Gdk.Pixbuf full = this.Load ();
+ Gdk.Pixbuf rotated = PixbufUtils.TransformOrientation (full, this.GetOrientation(), true);
+ Gdk.Pixbuf scaled = PixbufUtils.ScaleToMaxSize (rotated, width, height);
+ full.Dispose ();
+ return scaled;
+ }
+#endif
+ public void Select (SemWeb.StatementSink sink)
+ {
+ byte [] data = GetEmbeddedJpeg ();
+ if (data != null) {
+ System.IO.Stream stream = new System.IO.MemoryStream (data);
+ JpegHeader header = new JpegHeader (stream);
+ header.Select (sink);
+ }
+ }
+
+ private byte [] GetEmbeddedJpeg ()
+ {
+ using (System.IO.Stream stream = Open ()) {
+ stream.Position = 0x54;
+ byte [] data = new byte [24];
+ stream.Read (data, 0, data.Length);
+ uint jpeg_offset = BitConverter.ToUInt32 (data, 0, false);
+ uint jpeg_length = BitConverter.ToUInt32 (data, 4, false);
+
+ // FIXME implement wb parsing
+ //uint wb_offset = BitConverter.ToUInt32 (data, 8, false);
+ //uint wb_length = BitConverter.ToUInt32 (data, 12, false);
+
+ // FIXME implement decoding
+ //uint raw_offset = BitConverter.ToUInt32 (data, 16, false);
+ //uint raw_length = BitConverter.ToUInt32 (data, 20, false);
+
+ byte [] image = new byte [jpeg_length];
+ stream.Position = jpeg_offset;
+ stream.Read (image, 0, image.Length);
+ return image;
+ }
+
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/SvgFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/SvgFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,62 @@
+using System;
+
+namespace FSpot.Svg {
+ public class SvgFile : ImageFile // SemWeb.StatementSource
+ {
+ MetadataStore store;
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public SvgFile (Uri uri) : base (uri)
+ {
+ }
+
+ public SvgFile (string path) : base (path)
+ {
+ }
+
+ public MetadataStore Store {
+ get {
+ if (store == null) {
+ store = new MetadataStore ();
+ using (System.IO.Stream input = Open ()) {
+ Load (input);
+ }
+ }
+ return store;
+ }
+ }
+
+ public void Load (System.IO.Stream stream)
+ {
+ try {
+ store.Import (new SemWeb.RdfXmlReader (stream));
+ store.Dump ();
+
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+
+#if BROKEN_RSVG
+ public override Gdk.Pixbuf Load (int max_width, int max_height)
+ {
+ // FIXME this is a hack to work around a crash in the scaled
+ // gdk pixbuf rsvg loader. We load it without scaling it then scale the image
+ using (System.IO.Stream stream = Open ()) {
+ using (Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (stream)) {
+ Gdk.Pixbuf scaled = PixbufUtils.ScaleToMaxSize (pixbuf, max_width, max_height);
+ return scaled;
+ }
+ }
+ }
+#endif
+ public void Select (SemWeb.StatementSink sink)
+ {
+ Store.Select (sink);
+ }
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/Tiff.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/Tiff.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,2399 @@
+//#define DEBUG_LOADER
+using FSpot;
+using SemWeb;
+using System;
+using System.IO;
+using System.Collections.Generic;
+
+#if ENABLE_NUNIT
+using NUnit.Framework;
+#endif
+
+namespace FSpot.Tiff {
+
+ // This is primarily to preserve the names from the specification
+ // because they differ from the tiff standard names
+ public enum NiffId : ushort {
+ SubfileType = 0x00fe,
+ PelPathLength = 0x0100,
+ LineProgressionLength = 257,
+ BitsPerSample = 0x0101,
+ PhotometricInterpretation = 0x0106,
+ DataOffset = 0x0111,
+ SamplesPerPixel = 0x0115,
+ DataByteCounts = 0x0117,
+ PelPathResolution = 0x011a,
+ LineProgressionResolution = 0x011b,
+ ResolutionUnit = 0x0128,
+ ColumnsPerPelPath = 322,
+ RowsPerLineProgression = 323,
+ Rotation = 33465,
+ NavyCompression = 33466,
+ TileIndex = 33467
+ }
+
+ public enum TagGPS : ushort {
+ GPSVersionID = 0x0000,
+ GPSLatitudeRef = 0x0001,
+ GPSLatitude = 0x0002,
+ GPSLongitudeRef = 0x0003,
+ GPSLongitue = 0x0004,
+ GPSAltitudeRef = 0x0005,
+ GPSAltitude = 0x0006,
+ GPSTimeStamp = 0x0007,
+ GPSSatellites = 0x0008,
+ GPSStatus = 0x0009,
+ GPSMeasureMode = 0x000a,
+ GPSDOP = 0x000b,
+ GPSSpeedRef = 0x000c,
+ GPSSpeed = 0x000d,
+ GPSTrackRef = 0x000e,
+ GPSTrack = 0x000f,
+ GPSImgDirectionRef = 0x0010,
+ GPSImgDirection = 0x0011,
+ GPSMapDatum = 0x0012,
+ GPSDestLatitudeRef = 0x0013,
+ GPSDestLatitude = 0x0014,
+ GPSDestLongitudeRef = 0x0015,
+ GPSDestLongitude = 0x0016,
+ GPSDestBearingRef = 0x0017,
+ GPSDestBearing = 0x0018,
+ GPSDestDistanceRef = 0x0019,
+ GPSDestDistance = 0x001a,
+ GPSProcessingMethod = 0x001b,
+ GPSAreaInformation = 0x001c,
+ GPSDateStamp = 0x001d,
+ GPSDifferential = 0x001e
+ }
+
+ public enum TagId : ushort {
+ InteroperabilityIndex = 0x0001,
+ InteroperabilityVersion = 0x0002,
+
+ NewSubfileType = 0x00fe,
+ SubfileType = 0x00ff,
+
+ ImageWidth = 0x0100,
+ ImageLength = 0x0101,
+ BitsPerSample = 0x0102,
+ Compression = 0x0103,
+ PhotometricInterpretation = 0x0106,
+ FillOrder = 0x010a,
+ DocumentName = 0x010d,
+ ImageDescription = 0x010e,
+ Make = 0x010f,
+ Model = 0x0110,
+ StripOffsets = 0x0111,
+ Orientation = 0x0112,
+ SamplesPerPixel = 0x0115,
+ RowsPerStrip = 0x0116,
+ StripByteCounts = 0x0117,
+ XResolution = 0x011a,
+ YResolution = 0x011b,
+ PlanarConfiguration = 0x011c,
+
+ T4Options = 0x0124,
+ T6Options = 0x0125,
+
+ ResolutionUnit = 0x0128,
+ TransferFunction = 0x012d,
+ Software = 0x0131,
+ DateTime = 0x0132,
+ Artist = 0x013b,
+ WhitePoint = 0x013e,
+ PrimaryChromaticities = 0x013f,
+
+ HalftoneHints = 0x0141,
+ // Tiled images
+ TileWidth = 0x0142,
+ TileLength = 0x0143,
+ TileOffsets = 0x0144,
+ TileByteCounts = 0x0145,
+
+ SubIFDs = 0x014a, // TIFF-EP
+
+ // CMYK images
+ InkSet = 0x014c,
+ NumberOfInks = 0x014e,
+ InkNames = 0x014d,
+ DotRange = 0x0150,
+ TargetPrinter = 0x0151,
+ ExtraSamples = 0x0152,
+ SampleFormat = 0x0153,
+ SMinSampleValue = 0x0154,
+ SMaxSampleValue = 0x0155,
+
+ TransferRange = 0x0156,
+
+ ClipPath = 0x0157, // TIFF PageMaker Technote #2.
+
+ JPEGTables = 0x015b, // TIFF-EP
+
+ JPEGProc = 0x0200,
+ JPEGInterchangeFormat = 0x0201,
+ JPEGInterchangeFormatLength = 0x0202,
+ JPEGRestartInterval = 0x0203,
+ JPEGLosslessPredictors = 0x0205,
+ JPEGPointTransforms = 0x0206,
+ JPEGQTables = 0x0207,
+ JPEGDCTables = 0x0208,
+ JPEGACTables = 0x0209,
+
+ YCbCrCoefficients = 0x0211,
+ YCbCrSubSampling = 0x0212,
+ YCbCrPositioning = 0x0213,
+
+ ReferenceBlackWhite = 0x0214,
+ RelatedImageFileFormat = 0x1000,
+ RelatedImageWidth = 0x1001,
+ RelatedImageLength = 0x1002,
+ CFARepeatPatternDim = 0x828d,
+ CFAPattern = 0x828e,
+ BatteryLevel = 0x828f,
+ Copyright = 0x8298,
+ ExposureTime = 0x829a,
+ FNumber = 0x829d,
+
+ // These are from the NIFF spec and only really valid when the header begins with IIN1
+ // see the NiffTag enum for the specifcation specific names
+ Rotation = 0x82b9,
+ NavyCompression = 0x82ba,
+ TileIndex = 0x82bb,
+ // end NIFF specific
+
+ IPTCNAA = 0x83bb,
+
+ PhotoshopPrivate = 0x8649,
+
+ ExifIfdPointer = 0x8769,
+ InterColorProfile = 0x8773,
+ ExposureProgram = 0x8822,
+ SpectralSensitivity = 0x8824,
+ GPSInfoIfdPointer = 0x8825,
+ ISOSpeedRatings = 0x8827,
+ OECF = 0x8828,
+ ExifVersion = 0x9000,
+ DateTimeOriginal = 0x9003,
+ DateTimeDigitized = 0x9004,
+ ComponentsConfiguration = 0x9101,
+ CompressedBitsPerPixel = 0x9102,
+ ShutterSpeedValue = 0x9201,
+ ApertureValue = 0x9202,
+ BrightnessValue = 0x9203,
+ ExposureBiasValue = 0x9204,
+ MaxApertureValue = 0x9205,
+ SubjectDistance = 0x9206,
+ MeteringMode = 0x9207,
+ LightSource = 0x9208,
+ Flash = 0x9209,
+ FocalLength = 0x920a,
+
+ FlashEnergy_TIFFEP = 0x920b,// TIFF-EP
+ SpacialFrequencyResponse = 0x920c,// TIFF-EP
+ Noise = 0x920d,// TIFF-EP
+ FocalPlaneXResolution_TIFFEP = 0x920e,// TIFF-EP
+ FocalPlaneYResolution_TIFFEP = 0x920f,// TIFF-EP
+ FocalPlaneResolutionUnit_TIFFEP = 0x9210,// TIFF-EP
+ ImageName = 0x9211,// TIFF-EP
+ SecurityClassification = 0x9212,// TIFF-EP
+
+ ImageHistory = 0x9213, // TIFF-EP null separated list
+
+ SubjectArea = 0x9214,
+
+ ExposureIndex_TIFFEP = 0x9215, // TIFF-EP
+ TIFFEPStandardID = 0x9216, // TIFF-EP
+ SensingMethod_TIFFEP = 0x9217, // TIFF-EP
+
+ MakerNote = 0x927c,
+ UserComment = 0x9286,
+ SubSecTime = 0x9290,
+ SubSecTimeOriginal = 0x9291,
+ SubSecTimeDigitized = 0x9292,
+ FlashPixVersion = 0xa000,
+ ColorSpace = 0xa001,
+ PixelXDimension = 0xa002,
+ PixelYDimension = 0xa003,
+ RelatedSoundFile = 0xa004,
+ InteroperabilityIfdPointer = 0xa005,
+ FlashEnergy = 0xa20b,
+ SpatialFrequencyResponse = 0xa20c,
+ FocalPlaneXResolution = 0xa20e,
+ FocalPlaneYResolution = 0xa20f,
+ FocalPlaneResolutionUnit = 0xa210,
+ SubjectLocation = 0xa214,
+ ExposureIndex = 0xa215,
+ SensingMethod = 0xa217,
+ FileSource = 0xa300,
+ SceneType = 0xa301,
+ ExifCFAPattern = 0xa302,
+ CustomRendered = 0xa401,
+ ExposureMode = 0xa402,
+ WhiteBalance = 0xa403,
+ DigitalZoomRatio = 0xa404,
+ FocalLengthIn35mmFilm = 0xa405,
+ SceneCaptureType = 0xa406,
+ GainControl = 0xa407,
+ Contrast = 0xa408,
+ Saturation = 0xa409,
+ Sharpness = 0xa40a,
+ DeviceSettingDescription = 0xa40b,
+ SubjectDistanceRange = 0xa40c,
+ ImageUniqueId = 0xa420,
+
+ // The Following IDs are not described the EXIF spec
+ Gamma = 0xa500,
+
+ // The XMP spec declares that XMP data should live 0x2bc when
+ // embedded in tiff images.
+ XMP = 0x02bc,
+
+ // from the dng spec
+ DNGVersion = 0xc612, // Ifd0
+ DNGBackwardVersion = 0xc613, // Ifd0
+ UniqueCameraModel = 0xc614, // Ifd0
+ LocalizedCameraModel = 0xc615, // Ifd0
+ CFAPlaneColor = 0xc616, // RawIfd
+ CFALayout = 0xc617, // RawIfd
+ LinearizationTable = 0xc618, // RawIfd
+ BlackLevelRepeatDim = 0xc619, // RawIfd
+ BlackLevel = 0xc61a, // RawIfd
+ BlackLevelDeltaH = 0xc61b, // RawIfd
+ BlackLevelDeltaV = 0xc61c, // RawIfd
+ WhiteLevel = 0xc61d, // RawIfd
+ DefaultScale = 0xc61e, // RawIfd
+ DefaultCropOrigin = 0xc61f, // RawIfd
+ DefaultCropSize = 0xc620, // RawIfd
+ ColorMatrix1 = 0xc621, // Ifd0
+ ColorMatrix2 = 0xc622, // Ifd0
+ CameraCalibration1 = 0xc623, // Ifd0
+ CameraCalibration2 = 0xc624, // Ifd0
+ ReductionMatrix1 = 0xc625, // Ifd0
+ ReductionMatrix2 = 0xc626, // Ifd0
+ AnalogBalance = 0xc627, // Ifd0
+ AsShotNetural = 0xc628, // Ifd0
+ AsShotWhiteXY = 0xc629, // Ifd0
+ BaselineExposure = 0xc62a, // Ifd0
+ BaselineNoise = 0xc62b, // Ifd0
+ BaselineSharpness = 0xc62c, // Ifd0
+ BayerGreeSpit = 0xc62d, // Ifd0
+ LinearResponseLimit = 0xc62e, // Ifd0
+ CameraSerialNumber = 0xc62f, // Ifd0
+ LensInfo = 0xc630, // Ifd0
+ ChromaBlurRadius = 0xc631, // RawIfd
+ AntiAliasStrength = 0xc632, // RawIfd
+ DNGPrivateData = 0xc634, // Ifd0
+
+ MakerNoteSafety = 0xc635, // Ifd0
+
+ // The Spec says BestQualityScale is 0xc635 but it appears to be wrong
+ //BestQualityScale = 0xc635, // RawIfd
+ BestQualityScale = 0xc63c, // RawIfd this looks like the correct value
+
+ CalibrationIlluminant1 = 0xc65a, // Ifd0
+ CalibrationIlluminant2 = 0xc65b, // Ifd0
+
+ // Print Image Matching data
+ PimIfdPointer = 0xc4a5
+ }
+
+ public struct SRational {
+ public int Numerator;
+ public int Denominator;
+
+ public SRational (byte [] raw_data, int offset, bool little)
+ {
+ Numerator = BitConverter.ToInt32 (raw_data, offset, little);
+ Denominator = BitConverter.ToInt32 (raw_data, offset, little);
+ }
+
+ public SRational (int numerator, int denominator)
+ {
+ Numerator = numerator;
+ Denominator = denominator;
+ }
+
+ public static SRational BitwiseCopy (Rational rational)
+ {
+ SRational result;
+
+ result.Numerator = unchecked ((int) rational.Numerator);
+ result.Denominator = unchecked ((int) rational.Denominator);
+ return result;
+ }
+
+ public override string ToString ()
+ {
+ if (Numerator == 0 || Denominator == 0)
+ return String.Format ("{0}/{1}", Numerator, Denominator);
+ else if (Numerator % Denominator == 0)
+ return String.Format ("{0}", Numerator / Denominator);
+ else if (Denominator % Numerator == 0)
+ return String.Format ("1/{0}", Denominator / Numerator);
+
+ return String.Format ("{0}/{1}", Numerator, Denominator);
+ }
+
+ public double Value {
+ get {
+ return Numerator / (double)Denominator;
+ }
+ }
+ }
+
+ public struct Rational {
+ public uint Numerator;
+ public uint Denominator;
+
+ public Rational (uint numerator, uint denominator)
+ {
+ Numerator = numerator;
+ Denominator = denominator;
+ }
+
+ public Rational (string value)
+ {
+ string [] vals = value.Split ('/');
+ if (vals.Length == 2) {
+ this.Numerator = UInt32.Parse (vals [0]);
+ this.Denominator = UInt32.Parse (vals [1]);
+ return;
+ } if (vals.Length == 1) {
+ double tmp = Double.Parse (value);
+ this.Numerator = (uint) (tmp * 100000);
+ this.Denominator = 100000;
+ } else
+ throw new ParseException ("unable to parse rational value");
+ }
+
+ public override string ToString ()
+ {
+ if (Numerator == 0 || Denominator == 0)
+ return String.Format ("{0}/{1}", Numerator, Denominator);
+ else if (Numerator % Denominator == 0)
+ return String.Format ("{0}", Numerator / Denominator);
+ else if (Denominator % Numerator == 0)
+ return String.Format ("1/{0}", Denominator / Numerator);
+
+ return String.Format ("{0}/{1}", Numerator, Denominator);
+ }
+
+ public double Value {
+ get {
+ return Numerator / (double)Denominator;
+ }
+ }
+ }
+
+ struct UserComment {
+ string Charset;
+ public string Value;
+
+ public UserComment (string value)
+ {
+ Charset = null;
+ Value = value;
+ }
+
+ public UserComment (byte [] raw_data, bool little)
+ {
+ if (raw_data.Length == 8 || raw_data.Length == 0) {
+ Charset = null;
+ Value = String.Empty;
+ return;
+ } else if (raw_data.Length < 8) {
+ throw new Exception ("Invalid UserComment value, no charset found");
+ }
+
+ string charset = System.Text.Encoding.ASCII.GetString (raw_data, 0, 8);
+ System.Text.Encoding enc;
+
+ switch (charset) {
+ case "ASCII\0\0\0":
+ enc = System.Text.Encoding.ASCII;
+ break;
+ case "UNICODE\0":
+ case "Unicode\0":
+ enc = new System.Text.UnicodeEncoding (! little, true);
+ break;
+ case "JIS\0\0\0\0\0":
+ // FIXME this requires mono locale extras
+ try {
+ enc = System.Text.Encoding.GetEncoding ("euc-jp");
+ } catch {
+ System.Console.WriteLine ("missing jis0208 encoding");
+ enc = System.Text.Encoding.Default;
+ }
+ break;
+ case "\0\0\0\0\0\0\0\0":
+ // FIXME the spec says to use the local encoding in this case, we could probably
+ // do something smarter, but whatever.
+ enc = System.Text.Encoding.Default;
+ break;
+ default:
+ enc = null;
+ throw new ParseException (System.String.Format ("Invalid charset name: {0}", charset));
+ }
+
+ Charset = charset;
+ // for (int i = 0; i < raw_data.Length; i++)
+ // System.Console.WriteLine ("{0} - \"{1}\"", raw_data [i].ToString ("x"), raw_data [i]);
+ Value = enc.GetString (raw_data, 8, raw_data.Length - 8).Trim('\0');
+ }
+
+ public byte [] GetBytes (bool is_little)
+ {
+ bool ascii = true;
+ string description = Value;
+ System.Text.Encoding enc;
+ string heading;
+
+ for (int i = 0; i < description.Length; i++) {
+ if (description [i] > 127) {
+ ascii = false;
+ break;
+ }
+ }
+
+ if (ascii) {
+ heading = "ASCII\0\0\0";
+ enc = new System.Text.ASCIIEncoding ();
+ } else {
+ heading = "Unicode\0";
+ enc = new System.Text.UnicodeEncoding (! is_little, true);
+ }
+
+ int len = enc.GetByteCount (description);
+ byte [] data = new byte [len + heading.Length];
+ System.Text.Encoding.ASCII.GetBytes (heading, 0, heading.Length, data, 0);
+ enc.GetBytes (Value, 0, Value.Length, data, heading.Length);
+
+ UserComment c = new UserComment (data, is_little);
+ System.Console.WriteLine ("old = \"{0}\" new = \"{1}\" heading = \"{2}\"", c.Value, description, heading);
+ return data;
+ }
+
+ public override string ToString ()
+ {
+ return String.Format ("({0},charset={1})", Value, Charset);
+ }
+ }
+
+ public struct CFAPattern {
+ public ushort Rows;
+ public ushort Columns;
+ public byte [] Values;
+
+ public CFAPattern (byte [] raw_data, bool little)
+ {
+ Columns = BitConverter.ToUInt16 (raw_data, 0, little);
+ Rows = BitConverter.ToUInt16 (raw_data, 2, little);
+
+ Values = new byte [Rows * Columns];
+ // FIXME the contents here may differ from what we
+ // expect, but for now we won't throw an exception
+ // since the entry still may be a valid structure.
+ if (raw_data.Length -4 < Values.Length)
+ System.Array.Copy (raw_data, 4, Values, 0, Values.Length);
+ }
+
+ /*
+ * Note the Exif spec defines a CFA pattern tag that includes the row and column counts as shorts
+ * inside the first four bytes of the entry. The Tiff-EP standard define the CFARepeatPattern tag
+ * that contains the row and column counts presumably since the Exif version wouldn't allow you to
+ * alter the endian of the file without knowing the tag layout.
+ */
+
+ public CFAPattern (ushort rows, ushort cols, byte [] raw_data, bool little)
+ {
+ Columns = rows;
+ Rows = cols;
+ Values = new byte [rows * cols];
+ System.Array.Copy (raw_data, 0, Values, 0, Values.Length);
+ }
+ }
+
+ public struct OECFTable {
+ public ushort Rows;
+ public ushort Columns;
+ public string [] Names;
+ public SRational [] Values;
+
+ public OECFTable (byte [] raw_data, bool little)
+ {
+ Columns = BitConverter.ToUInt16 (raw_data, 0, little);
+ Rows = BitConverter.ToUInt16 (raw_data, 2, little);
+ Names = new string [Columns];
+ Values = new SRational [Columns * Rows];
+
+ int pos = 2;
+ int i;
+ int type_size = DirectoryEntry.GetTypeSize (EntryType.SRational);
+
+ for (i = 0; i < Names.Length; i++)
+ Names [i] = ReadString (raw_data, ref pos);
+
+ for (i = 0; i < Values.Length; i++)
+ Values [i] = new SRational (raw_data, pos + i * type_size, little);
+
+ }
+
+ public string ReadString (byte [] data, ref int pos)
+ {
+ int start = pos;
+ for (; pos < data.Length; pos++) {
+ if (data [pos] == 0)
+ break;
+ }
+ return System.Text.Encoding.ASCII.GetString (data, start, pos - start);
+ }
+ }
+
+ public enum ExtraSamples {
+ Unspecified = 0,
+ AssociatedAlpha = 1,
+ UnassociatedAlpa = 2
+ }
+
+ public enum FileSource {
+ DCF = 3,
+ }
+
+ public enum PhotometricInterpretation : ushort {
+ WhiteIsZero = 0,
+ BlackIsZero = 1,
+ RGB = 2,
+ PaletteColor = 3,
+ TransparencyMask = 4,
+ Separated = 5, // CMYK
+ YCbCr = 6,
+ CIELab = 8,
+ ICCLab = 9,
+ ITULab = 10,
+ LogL = 32844, // Log Luminance
+ LogLUV = 32845,
+ ColorFilterArray = 32803, // ColorFilterArray... the good stuff
+ LinearRaw = 34892 // DBG LinearRaw
+ }
+
+ public enum PlanarConfiguration {
+ Chunky = 1,
+ Planar = 2
+ }
+
+ public enum Compression {
+ Packed = 1,
+ Huffman = 2,
+ T4 = 3,
+ T6 = 4,
+ LZW = 5,
+ JPEG = 6,
+ JPEGStream = 7, // TIFF-EP stores full jpeg stream
+ Deflate = 8,
+ JBIG = 9,
+ JBIG_MRC,
+ PackBits = 32773,
+ NikonCompression = 34713,
+ Deflate_experimental = 0x80b2
+ }
+
+ public enum JPEGProc {
+ BaselineSequencial = 1,
+ LosslessHuffman = 14,
+ }
+
+ public enum SubfileType {
+ FullResolution = 1,
+ ReducedResolution = 2,
+ PageOfMultipage = 3
+ }
+
+ public enum ExposureProgram {
+ NotDefined = 0,
+ Manual = 1,
+ NormalProgram = 2, // Normal Program
+ AperturePriority = 3, // Aperture priority
+ ShutterPriorty = 4, // Shutter priority
+ CreativeProgram = 5, // Creative program
+ ActionProgram = 6, // Action program
+ PortraitMode = 7, // Portrait mode
+ LandscapeMode = 8 // Landscape mode
+ }
+
+ public enum ExposureMode {
+ Auto = 0,
+ Manual = 1,
+ AutoBracket = 2
+ }
+
+ public enum CustomRendered : ushort {
+ Normal = 0,
+ Custom = 1
+ }
+
+ public enum SceneType {
+ DirectlyPhotographed = 1
+ }
+
+ public enum MeteringMode {
+ Uknown = 0,
+ Average = 1,
+ CenterWeightedAverage = 2,
+ Spot = 3,
+ MulitSpot = 4,
+ Pattern = 5,
+ Partial = 6,
+ }
+
+ public enum SceneCaptureType : ushort {
+ Standard = 0,
+ Landscape = 1,
+ Portrait = 2,
+ NightScene = 3
+ }
+
+ public enum GainControl : ushort {
+ None = 0,
+ LowGainUp = 1,
+ HighGainUp = 2,
+ LowGainDown = 3,
+ HighGainDown = 4
+ }
+
+ public enum Contrast : ushort {
+ Normal = 0,
+ Soft = 1,
+ Hard = 2
+ }
+
+ public enum Saturation : ushort {
+ Normal = 0,
+ Low = 1,
+ High = 2
+ }
+
+ public enum WhiteBalance : ushort {
+ Auto = 0,
+ Manual = 1
+ }
+
+ public enum Sharpness : ushort {
+ Normal = 0,
+ Soft = 1,
+ Hard = 2
+ }
+
+ public enum LightSource {
+ Unknown = 0,
+ Daylight = 1,
+ Fluorescent = 2,
+ Tungsten = 3,
+ Fash = 4,
+ FineWeather = 9,
+ CloudyWeather = 10,
+ Shade = 11,
+ DaylightFluorescent = 12,
+ DaylightWhiteFluorescent = 13,
+ CoolWhiteFluorescent = 14,
+ WhiteFluorescent = 15,
+ StandardLightA = 17,
+ StandardLightB = 18,
+ StandardLightC = 19,
+ D55 = 20,
+ D65 = 21,
+ D75 = 22,
+ D50 = 23,
+ ISOStudioTungsten = 24,
+ OtherSource = 255
+ }
+
+ public enum ColorSpace {
+ StandardRGB = 1, // sRGB
+ AdobeRGB = 2,
+ Uncalibrated = 0xffff
+ }
+
+ public enum ComponentsConfiguration {
+ DoesNotExist = 0,
+ Y = 1,
+ Cb = 2,
+ Cr = 3,
+ R = 4,
+ G = 6,
+ }
+
+ public enum ResolutionUnit : ushort {
+ Uncalibrated = 1,
+ Inch = 2,
+ Centimeter = 3
+ }
+
+ public enum SensingMethod : short {
+ NotDefined = 1,
+ OneChipColorAreaSensor = 2,
+ TwoChipColorAreaSensor = 3,
+ ThreeChipColorAreaSensor = 4,
+ ColorSequentialAreaSensor = 5,
+ TrilinearSensor = 7,
+ ColorSequentialLinearSensor = 8
+ }
+
+ [System.Flags]
+ public enum NewSubfileType : uint {
+ ReducedResolution = 1,
+ PageOfMultipage= 1 << 1,
+ TransparencyMask = 1 << 2
+ }
+
+ public enum EntryType {
+ Byte = 1,
+ Ascii,
+ Short,
+ Long,
+ Rational,
+ SByte,
+ Undefined,
+ SShort,
+ SLong,
+ SRational,
+ Float,
+ Double,
+ Ifd // TIFF-EP - TIFF PageMaker TechnicalNote 2
+ }
+
+ public class Tag {
+ public ushort Id;
+ public EntryType Type;
+ public int Count;
+ public string Name;
+ public string Description;
+ }
+
+ public class CanonTag : Tag {
+ // http://www.gvsoft.homedns.org/exif/makernote-canon.html
+
+ public enum CanonId {
+ Unknown1 = 0x0000,
+ CameraSettings1 = 0x0001,
+ Unknown2 = 0x0003,
+ CameraSettings2 = 0x0004,
+ ImageType = 0x0006,
+ FirmwareVersion = 0x0007,
+ ImageNumber = 0x0008,
+ OwnerName = 0x0009,
+ Unknown3 = 0x000a,
+ CameraSerialNumber = 0x000c,
+ Unknown4 = 0x000d,
+ CustomFunctions = 0x000f
+ }
+
+ public CanonTag (CanonId id, EntryType type, int count, string name, string description)
+ {
+ this.Id = (ushort)id;
+ this.Type = type;
+ this.Count = count;
+ this.Name = name;
+ this.Description = description;
+ }
+
+ public static System.Collections.Hashtable Tags;
+
+ static CanonTag () {
+ CanonTag [] tags = {
+ new CanonTag (CanonId.Unknown1, EntryType.Short, 6, null, null),
+ new CanonTag (CanonId.CameraSettings1, EntryType.Short, -1, "Camera Settings 1", "First Canon MakerNote settings section"),
+ new CanonTag (CanonId.Unknown2, EntryType.Short, 4, null, null),
+ new CanonTag (CanonId.CameraSettings2, EntryType.Short, -1, "Camera Settings 2", "Second Canon MakerNote settings section"),
+ new CanonTag (CanonId.ImageType, EntryType.Ascii, 32, "Image Type", null), // FIXME description
+ new CanonTag (CanonId.FirmwareVersion, EntryType.Ascii, 24, "Firmware Version", "Version of the firmware installed on the camera"),
+ new CanonTag (CanonId.ImageNumber, EntryType.Long, 1, "Image Number", null), // FIXME description
+ new CanonTag (CanonId.OwnerName, EntryType.Long, 32, "Owner Name", "Name of the Camera Owner"), // FIXME description
+ new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
+ new CanonTag (CanonId.CameraSerialNumber, EntryType.Short, 1, "Serial Number", null), //FIXME description
+ new CanonTag (CanonId.Unknown4, EntryType.Short, -1, null, null),
+ new CanonTag (CanonId.CustomFunctions, EntryType.Short, -1, "Custom Functions", "Camera Custom Functions")
+ };
+
+ foreach (CanonTag tag in tags)
+ Tags [tag.Id] = tag;
+ }
+
+ }
+
+ public enum Endian {
+ Big,
+ Little
+ }
+
+ public class ParseException : System.Exception
+ {
+ public ParseException (string msg) : base (msg)
+ {
+ }
+ }
+
+ public class ShortReadException : ParseException
+ {
+ public ShortReadException () : base ("Short Read")
+ {
+ }
+ }
+
+ public class Converter {
+ public static uint ReadUInt (System.IO.Stream stream, Endian endian)
+ {
+ byte [] tmp = new byte [4];
+
+ if (stream.Read (tmp, 0, tmp.Length) < 4) {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("short read XXXXXXXXXXXXXXXXXXXXXXx");
+#endif
+ throw new ShortReadException ();
+ }
+ return BitConverter.ToUInt32 (tmp, 0, endian == Endian.Little);
+ }
+
+ public static ushort ReadUShort (System.IO.Stream stream, Endian endian)
+ {
+ byte [] tmp = new byte [2];
+
+ if (stream.Read (tmp, 0, tmp.Length) < 2) {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("Short read");
+#endif
+ throw new ShortReadException ();
+ }
+
+ return BitConverter.ToUInt16 (tmp, 0, endian == Endian.Little);
+ }
+ }
+
+ public class Header : SemWeb.StatementSource {
+ public Endian endian;
+
+ private uint directory_offset;
+ public ImageDirectory Directory;
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public Header (System.IO.Stream stream)
+ {
+ //using (new Timer ("new Tiff.Header")) {
+ byte [] data = new byte [8];
+ stream.Read (data, 0, data.Length);
+ if (data [0] == 'M' && data [1] == 'M')
+ endian = Endian.Big;
+ else if (data [0] == 'I' && data [1] == 'I')
+ endian = Endian.Little;
+
+ ushort marker = BitConverter.ToUInt16 (data, 2, endian == Endian.Little);
+ switch (marker) {
+ case 42:
+ //System.Console.WriteLine ("Found Standard Tiff Marker {0}", marker);
+ break;
+ case 0x4f52:
+ System.Console.WriteLine ("Found Olympus Tiff Marker {0}", marker.ToString ("x"));
+ break;
+ case 0x4e31:
+ System.Console.WriteLine ("Found Navy Interchange File Format Tiff Marker {0}", marker.ToString ("x"));
+ break;
+ default:
+ System.Console.WriteLine ("Found Unknown Tiff Marker {0}", marker.ToString ("x"));
+ break;
+ }
+
+ //System.Console.WriteLine ("Converting Something");
+ directory_offset = BitConverter.ToUInt32 (data, 4, endian == Endian.Little);
+
+ if (directory_offset < 8)
+ throw new ParseException ("Invalid IFD0 Offset [" + directory_offset.ToString () + "]");
+
+#if DEBUG_LOADER
+ System.Console.WriteLine ("Reading First IFD");
+#endif
+ Directory = new ImageDirectory (stream, directory_offset, endian);
+ //}
+ }
+
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ //using (new Timer ("Tiff.Header.Select")) {
+ SelectDirectory (Directory, sink);
+ //}
+ }
+
+ public void SelectDirectory (ImageDirectory dir, StatementSink sink)
+ {
+ foreach (DirectoryEntry e in dir.Entries) {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("{0}", e.Id);
+#endif
+ switch (e.Id) {
+ case TagId.IPTCNAA:
+ System.IO.Stream iptcstream = new System.IO.MemoryStream (e.RawData);
+ FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
+ iptc.Select (sink);
+ break;
+ case TagId.PhotoshopPrivate:
+ System.IO.Stream bimstream = new System.IO.MemoryStream (e.RawData);
+ FSpot.Bim.BimFile bim = new FSpot.Bim.BimFile (bimstream);
+ bim.Select (sink);
+ break;
+ case TagId.XMP:
+ System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
+ FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
+ xmp.Select (sink);
+ break;
+ case TagId.ImageDescription:
+ MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt",
+ new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
+ break;
+ case TagId.UserComment:
+ MetadataStore.AddLiteral (sink, "exif:UserComment", "rdf:Alt",
+ new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
+ break;
+ case TagId.Copyright:
+ MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt",
+ new SemWeb.Literal (e.ValueAsString [0], "x-default", null));
+ break;
+ case TagId.Artist:
+ MetadataStore.Add (sink, "dc:creator", "rdf:Seq", e.ValueAsString);
+ break;
+ case TagId.ExifIfdPointer:
+ try {
+ ImageDirectory sub = ((SubdirectoryEntry)e).Directory [0];
+ SelectDirectory (sub, sink);
+ } catch (System.Exception exc) {
+ System.Console.WriteLine (exc);
+ }
+ break;
+ case TagId.Software:
+ MetadataStore.AddLiteral (sink, "xmp:CreatorTool", e.ValueAsString [0]);
+ break;
+ case TagId.DateTime:
+ try {
+
+ MetadataStore.AddLiteral (sink, "xmp:ModifyDate",
+ e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
+ } catch (System.Exception ex) {
+ System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.ValueAsString[0], ex, Environment.NewLine));
+ }
+
+ break;
+ case TagId.DateTimeOriginal:
+ case TagId.DateTimeDigitized:
+ // FIXME subsectime needs to be included in these values
+ // FIXME shouldn't DateTimeOriginal be xmp:CreateDate? the spec says no but wtf?
+ try {
+ MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (),
+ e.ValueAsDate.ToString ("yyyy-MM-ddThh:mm:ss"));
+ } catch (System.Exception ex) {
+ System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.ValueAsString[0], ex, Environment.NewLine));
+ }
+ break;
+ //case TagId.SpatialFrequencyResponse
+ case TagId.ExifCFAPattern:
+ CFAPattern pattern = new CFAPattern (e.RawData, e.IsLittle);
+ Entity empty = new BNode ();
+ Statement top = new Statement (MetadataStore.FSpotXMPBase,
+ (Entity)MetadataStore.Namespaces.Resolve ("exif:" + e.Id.ToString ()),
+ empty);
+
+ Statement cols = new Statement (empty,
+ (Entity) MetadataStore.Namespaces.Resolve ("exif:Columns"),
+ new SemWeb.Literal (pattern.Columns.ToString (), null, null));
+ sink.Add (cols);
+ Statement rows = new Statement (empty,
+ (Entity) MetadataStore.Namespaces.Resolve ("exif:Rows"),
+ new SemWeb.Literal (pattern.Rows.ToString (), null, null));
+ sink.Add (rows);
+ string [] vals = e.ArrayToString (pattern.Values);
+ MetadataStore.Add (sink, empty, "exif:Values", "rdf:Seq", vals);
+ sink.Add (top);
+ break;
+ case TagId.ExifVersion:
+ case TagId.FlashPixVersion:
+ case TagId.ColorSpace:
+ case TagId.CompressedBitsPerPixel:
+ case TagId.PixelYDimension:
+ case TagId.PixelXDimension:
+ case TagId.RelatedSoundFile:
+ case TagId.ExposureTime:
+ case TagId.FNumber:
+ case TagId.ExposureProgram:
+ case TagId.SpectralSensitivity:
+ case TagId.ShutterSpeedValue:
+ case TagId.ApertureValue:
+ case TagId.BrightnessValue:
+ case TagId.ExposureBiasValue:
+ case TagId.MaxApertureValue:
+ case TagId.SubjectDistance:
+ case TagId.MeteringMode:
+ case TagId.LightSource:
+ case TagId.FocalLength:
+ case TagId.FlashEnergy:
+ case TagId.FocalPlaneXResolution:
+ case TagId.FocalPlaneYResolution:
+ case TagId.FocalPlaneResolutionUnit:
+ case TagId.ExposureIndex:
+ case TagId.SensingMethod:
+ case TagId.FileSource:
+ case TagId.SceneType:
+ case TagId.CustomRendered:
+ case TagId.ExposureMode:
+ case TagId.WhiteBalance:
+ case TagId.DigitalZoomRatio:
+ case TagId.FocalLengthIn35mmFilm:
+ case TagId.SceneCaptureType:
+ case TagId.GainControl:
+ case TagId.Contrast:
+ case TagId.Saturation:
+ case TagId.Sharpness:
+ MetadataStore.AddLiteral (sink, "exif:" + e.Id.ToString (), e.ValueAsString [0]);
+ break;
+ case TagId.ComponentsConfiguration:
+ case TagId.ISOSpeedRatings:
+ case TagId.SubjectArea:
+ case TagId.SubjectLocation:
+ MetadataStore.Add (sink, "exif:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
+ break;
+ case TagId.TransferFunction:
+ case TagId.YCbCrSubSampling:
+ case TagId.WhitePoint:
+ case TagId.PrimaryChromaticities:
+ case TagId.YCbCrCoefficients:
+ case TagId.ReferenceBlackWhite:
+ case TagId.BitsPerSample:
+ MetadataStore.Add (sink, "tiff:" + e.Id.ToString (), "rdf:Seq", e.ValueAsString);
+ break;
+ case TagId.Orientation:
+ case TagId.Compression:
+ case TagId.PhotometricInterpretation:
+ case TagId.SamplesPerPixel:
+ case TagId.PlanarConfiguration:
+ case TagId.YCbCrPositioning:
+ case TagId.ResolutionUnit:
+ case TagId.ImageWidth:
+ case TagId.ImageLength:
+ case TagId.Model:
+ case TagId.Make:
+ try {
+ MetadataStore.AddLiteral (sink, "tiff:" + e.Id.ToString (), e.ValueAsString [0]);
+ } catch (System.Exception ex) {
+ System.Console.WriteLine (String.Format ("error parsing {0}{2}{1}", e.Id, ex, Environment.NewLine));
+ }
+ break;
+ }
+ }
+ }
+
+ public void Save (System.IO.Stream out_stream)
+ {
+ OrderedWriter writer = new OrderedWriter (out_stream, endian == Endian.Little);
+
+ /* Header */
+ if (endian == Endian.Little) {
+ writer.Write ((byte)'I');
+ writer.Write ((byte)'I');
+ } else {
+ writer.Write ((byte)'M');
+ writer.Write ((byte)'M');
+ }
+
+ writer.Write ((ushort)42);
+
+ /* First IFD */
+ Directory.Save (writer, 8);
+ }
+
+ public void Dump (string name)
+ {
+ ImageDirectory ifd = Directory;
+ for (int i = 0; ifd != null; i++) {
+ ifd.Dump (System.String.Format ("IFD[{0}]:", i));
+ ifd = ifd.NextDirectory;
+ }
+ }
+ }
+
+ public class ImageDirectory {
+ protected Endian endian;
+ protected ushort num_entries;
+ protected List<DirectoryEntry> entries;
+ protected uint orig_position;
+
+ protected uint next_directory_offset;
+ ImageDirectory next_directory;
+
+ protected bool has_header;
+ protected bool has_footer;
+
+ public ImageDirectory (System.IO.Stream stream, uint start_position, Endian endian)
+ {
+ this.endian = endian;
+ orig_position = start_position;
+ Load (stream);
+ }
+
+ public uint Save (OrderedWriter writer, uint position)
+ {
+ writer.Write (position);
+ writer.Stream.Position = position;
+
+ writer.Write ((ushort)entries.Count);
+
+ position += 2;
+ uint value_position = (uint) (position + 12 * entries.Count + 4);
+
+ for (int i = 0; i < entries.Count; i++) {
+ writer.Stream.Position = position + (12 * i);
+ value_position = (uint)Entries[i].Save (writer, value_position);
+ }
+
+ writer.Stream.Position = position + (12 * entries.Count);
+ if (next_directory != null)
+ value_position = next_directory.Save (writer, value_position);
+ else
+ writer.Write ((uint) 0);
+
+ return value_position;
+ }
+
+ protected void Load (System.IO.Stream stream)
+ {
+ ReadHeader (stream);
+ ReadEntries (stream);
+ ReadFooter (stream);
+
+ LoadEntries (stream);
+ LoadNextDirectory (stream);
+ }
+
+ public virtual bool ReadHeader (System.IO.Stream stream)
+ {
+ stream.Seek ((long)orig_position, System.IO.SeekOrigin.Begin);
+ return true;
+ }
+
+ protected virtual void ReadEntries (System.IO.Stream stream)
+ {
+ num_entries = Converter.ReadUShort (stream, endian);
+#if DEBUG_LOADER
+ System.Console.WriteLine ("reading {0} entries", num_entries);
+#endif
+ entries = new List<DirectoryEntry> (num_entries);
+ int entry_length = num_entries * 12;
+ byte [] content = new byte [entry_length];
+
+ if (stream.Read (content, 0, content.Length) < content.Length) {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("short read XXXXXXXXXXXXXXXXXXXXXXx");
+#endif
+ throw new ShortReadException ();
+ }
+
+
+ for (int pos = 0; pos < entry_length; pos += 12) {
+ DirectoryEntry entry = CreateEntry (this, content, pos, this.endian);
+ entries.Add (entry);
+#if DEBUG_LOADER
+ System.Console.WriteLine ("Added Entry {0} {1} - {2} * {3}", entry.Id.ToString (), entry.Id.ToString ("x"), entry.Type, entry.Count);
+#endif
+ if (entry.Id == TagId.NewSubfileType) {
+
+ }
+ }
+ }
+
+ protected virtual void ReadFooter (System.IO.Stream stream)
+ {
+ next_directory_offset = Converter.ReadUInt (stream, this.endian);
+ }
+
+ protected void LoadEntries (System.IO.Stream stream)
+ {
+ foreach (DirectoryEntry entry in entries) {
+ entry.LoadExternal (stream);
+ }
+ }
+
+ protected void LoadNextDirectory (System.IO.Stream stream)
+ {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("start_position = {1} next_directory_offset = {0}",
+ next_directory_offset, orig_position);
+#endif
+ next_directory = null;
+ try {
+ if (next_directory_offset != 0 && next_directory_offset != orig_position)
+ next_directory = new ImageDirectory (stream, next_directory_offset, this.endian);
+
+ } catch (System.Exception) {
+ //System.Console.WriteLine ("Error loading directory {0}", e.ToString ());
+ next_directory = null;
+ next_directory_offset = 0;
+ }
+ }
+
+ public ImageDirectory NextDirectory {
+ get {
+ return next_directory;
+ }
+ }
+
+ public List<DirectoryEntry> Entries {
+ get {
+ return entries;
+ }
+ }
+
+ public DirectoryEntry Lookup (TagId id)
+ {
+ foreach (DirectoryEntry entry in entries)
+ if (entry.Id == id)
+ return entry;
+
+
+ return null;
+ }
+
+ private DirectoryEntry GetEntry (int i)
+ {
+ if (i < Entries.Count)
+ return Entries [i];
+ else
+ return null;
+ }
+
+ public DirectoryEntry Lookup (uint id)
+ {
+ foreach (DirectoryEntry entry in entries)
+ if ((uint)entry.Id == id)
+ return entry;
+
+ return null;
+ }
+
+ public static DirectoryEntry CreateEntry (ImageDirectory parent, byte [] input, int start, Endian header_endian)
+ {
+ TagId tagid;
+ EntryType type;
+
+ DirectoryEntry.ParseHeader (input, start, out tagid, out type, header_endian);
+ //ConstructorFunc ctor = ctors[tagid];
+ //if (ctor == null) {
+ // return ctor (input, header_endian);
+ //}
+
+ switch (tagid) {
+ case TagId.ExifIfdPointer:
+ case TagId.GPSInfoIfdPointer:
+ case TagId.InteroperabilityIfdPointer:
+ case TagId.SubIFDs:
+ return new SubdirectoryEntry (input, start, header_endian);
+ //case TagId.MakerNote:
+ //return new MakerNoteEntry (input, start, header_endian);
+ //case TagId.PimIfdPointer:
+ //return new
+ //case TagId.MakerNote:
+ //return new MakerNoteEntry (input, start, header_endian);
+ }
+
+ switch (type) {
+ case EntryType.Ifd:
+ //System.Console.WriteLine ("Trying to load {0} {1}", tagid, tagid.ToString ("x"));
+ return new SubdirectoryEntry (input, start, header_endian);
+ case EntryType.Byte:
+ return new ByteEntry (input, start, header_endian);
+ case EntryType.Long:
+ return new LongEntry (input, start, header_endian);
+ case EntryType.Short:
+ return new ShortEntry (input, start, header_endian);
+ }
+
+ return new DirectoryEntry (input, start, header_endian);
+ }
+
+#if false
+ public Cms.Profile GetProfile ()
+ {
+ Cms.ColorCIExyY whitepoint = new Cms.ColorCIExyY (0, 0, 0);
+ Cms.ColorCIExyYTriple primaries = new Cms.ColorCIExyYTriple (whitepoint, whitepoint, whitepoint);
+ Cms.GammaTable [] transfer = null;
+ int bits_per_sample = 8;
+ double gamma = 2.2;
+
+ foreach (DirectoryEntry e in entries) {
+ switch (e.Id) {
+ case TagId.InterColorProfile:
+ try {
+ return new Cms.Profile (e.RawData);
+ } catch (System.Exception ex) {
+ System.Console.WriteLine (ex);
+ }
+ break;
+ case TagId.ColorSpace:
+ switch ((ColorSpace)e.ValueAsLong [0]) {
+ case ColorSpace.StandardRGB:
+ return Cms.Profile.CreateStandardRgb ();
+ case ColorSpace.AdobeRGB:
+ return Cms.Profile.CreateAlternateRgb ();
+ case ColorSpace.Uncalibrated:
+ System.Console.WriteLine ("Uncalibrated colorspace");
+ break;
+ }
+ break;
+ case TagId.WhitePoint:
+ Rational [] white = e.RationalValue;
+ whitepoint.x = white [0].Value;
+ whitepoint.y = white [1].Value;
+ whitepoint.Y = 1.0;
+ break;
+ case TagId.PrimaryChromaticities:
+ Rational [] colors = e.RationalValue;
+ primaries.Red.x = colors [0].Value;
+ primaries.Red.y = colors [1].Value;
+ primaries.Red.Y = 1.0;
+
+ primaries.Green.x = colors [2].Value;
+ primaries.Green.y = colors [3].Value;
+ primaries.Green.Y = 1.0;
+
+ primaries.Blue.x = colors [4].Value;
+ primaries.Blue.y = colors [5].Value;
+ primaries.Blue.Y = 1.0;
+ break;
+ case TagId.TransferFunction:
+ ushort [] trns = e.ShortValue;
+ ushort gamma_count = (ushort) (1 << bits_per_sample);
+ Cms.GammaTable [] tables = new Cms.GammaTable [3];
+ System.Console.WriteLine ("Parsing transfer function: count = {0}", trns.Length);
+
+ // FIXME we should use the TransferRange here
+ // FIXME we should use bits per sample here
+ for (int c = 0; c < 3; c++) {
+ tables [c] = new Cms.GammaTable (trns, c * gamma_count, gamma_count);
+ }
+
+ transfer = tables;
+ break;
+ case TagId.ExifIfdPointer:
+ SubdirectoryEntry exif = (SubdirectoryEntry) e;
+ DirectoryEntry ee = exif.Directory [0].Lookup ((int)TagId.Gamma);
+
+ if (ee == null)
+ break;
+
+ Rational rgamma = ee.RationalValue [0];
+ gamma = rgamma.Value;
+ break;
+ }
+ }
+
+ if (transfer == null) {
+ Cms.GammaTable basic = new Cms.GammaTable (1 << bits_per_sample, gamma);
+ transfer = new Cms.GammaTable [] { basic, basic, basic };
+ }
+
+ // if we didn't get a white point or primaries, give up
+ if (whitepoint.Y != 1.0 || primaries.Red.Y != 1.0)
+ return null;
+
+ return new Cms.Profile (whitepoint, primaries, transfer);
+ }
+#endif
+
+ public void Dump (string name)
+ {
+ System.Console.WriteLine ("Starting {0}", name);
+ foreach (DirectoryEntry e in this.Entries)
+ e.Dump (name);
+ System.Console.WriteLine ("Ending {0}", name);
+ }
+
+ public string Dump2 ()
+ {
+ System.Text.StringBuilder builder = new System.Text.StringBuilder ();
+ builder.Append ("Dummping IFD");
+ foreach (DirectoryEntry entry in entries) {
+ builder.Append (entry.ToString ()+ Environment.NewLine);
+
+ if (entry is SubdirectoryEntry)
+ builder.Append ("Found SUBDIRECTORYENTRY" + Environment.NewLine);
+ }
+
+ if (next_directory != null) {
+ builder.Append ("Dummping Next IFD");
+ builder.Append (next_directory.Dump2 ());
+ }
+
+ return builder.ToString ();
+ }
+ }
+
+ public class MakerNoteEntry : SubdirectoryEntry {
+ public MakerNoteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
+ {
+
+ }
+
+ public override uint GetEntryCount ()
+ {
+ return 1;
+ }
+
+ public override void LoadExternal (System.IO.Stream stream)
+ {
+ }
+ }
+
+
+ public class SubdirectoryEntry : DirectoryEntry {
+ public uint directory_offset;
+ public ImageDirectory [] Directory;
+
+ public SubdirectoryEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
+ {
+ if (this.GetEntryCount () > 1) {
+ System.Console.WriteLine ("Count is greater than 1 ({1}) on Subdirectory {0} interesting", tagid, count);
+ }
+ }
+
+ public override uint Save (OrderedWriter writer, uint position)
+ {
+#if DEBUG_LOADER
+ Console.WriteLine ("writing entry {0} {1} {2} - value offset = {3}", Id, Type, Count, position);
+#endif
+
+ writer.Write ((ushort)Id);
+ writer.Write ((ushort)Type);
+ writer.Write ((uint)Count);
+
+
+ if (Directory.Length == 1) {
+ writer.Write ((uint)position);
+ position = Directory [0].Save (writer, position);
+ } else if (Directory.Length > 1) {
+ writer.Write ((uint)position);
+ uint value_position = (uint) (position + Directory.Length * 4);
+ for (int i = 0; i < Directory.Length; i++) {
+ writer.Stream.Position = position + i * 4;
+ value_position = Directory [i].Save (writer, value_position);
+ }
+ return value_position;
+ } else
+ writer.Write ((uint) 0);
+
+ return position;
+ }
+
+ public virtual uint GetEntryCount ()
+ {
+ return count;
+ }
+
+ public override void LoadExternal (System.IO.Stream stream)
+ {
+ uint entry_count = GetEntryCount ();
+ Directory = new ImageDirectory [entry_count];
+
+ base.LoadExternal (stream);
+
+ for (int i = 0; i < entry_count; i++) {
+ try {
+ directory_offset = BitConverter.ToUInt32 (raw_data, i * 4, endian == Endian.Little);
+ if (directory_offset == offset_origin + 8)
+ throw new Exception ("recursive ifd");
+ Directory [i] = new ImageDirectory (stream, directory_offset, endian);
+ } catch (System.Exception e) {
+ System.Console.WriteLine ("Error loading Subdirectory {0} at {2} of {3}bytes:{4}{1}",
+ this.Id, e, directory_offset, stream.Length, Environment.NewLine);
+ }
+
+ }
+ }
+
+ public override void Dump (string name)
+ {
+ for (int i = 0; i < GetEntryCount (); i++) {
+ string subdirname = System.String.Format ("{0}{1}[{2}]({3})]", name, tagid, i, directory_offset);
+
+ try {
+ if (Directory [i] != null)
+ Directory [i].Dump (subdirname);
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e);
+ }
+ }
+ }
+ }
+
+ public class ShortEntry : DirectoryEntry {
+ public ShortEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
+ {
+ }
+ }
+
+ public class LongEntry : DirectoryEntry {
+ public LongEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
+ {
+ if (type != EntryType.Long)
+ throw new ParseException (System.String.Format ("Invalid Settings At Birth {0}", tagid));
+ }
+ }
+
+ public class ByteEntry : DirectoryEntry {
+ public ByteEntry (byte [] data, int offset, Endian endian) : base (data, offset, endian)
+ {
+ if (type != EntryType.Byte)
+ throw new ParseException ("Invalid Settings At Birth");
+ }
+ }
+
+#if false
+ public class ImageLoader {
+ int width;
+ int length;
+ int [] bps;
+ PhotometricInterpretation interpretation;
+ Compression compression;
+ uint [] offsets;
+ uint [] strip_byte_counts;
+ uint rows_per_strip;
+ byte [] strip;
+
+ public ImageLoader (ImageDirectory directory)
+ {
+ width = directory.Lookup (TagId.ImageWidth).ValueAsLong [0];
+ length = directory.Lookup (TagId.ImageLength).ValueAsLong [0];
+ bps = directory.Lookup (TagId.BitsPerSample).ValueAsLong;
+
+ compression = (Compression) directory.Lookup (TagId.Compression).ValueAsLong [0];
+ interpretation = (PhotometricInterpretation) directory.Lookup (TagId.PhotometricInterpretation).ValueAsLong [0];
+
+ offsets = directory.Lookup (TagId.StripOffsets).ValueAsLong;
+ strip_byte_counts = directory.Lookup (TagId.StripByteCounts).ValueAsLong;
+ rows_per_strip = directory.Lookup (TagId.RowsPerStrip).ValueAsLong [0];
+
+ if (interpretation !=
+ }
+
+
+#if false
+ public Gdk.Pixbuf LoadPixbuf (System.IO.Stream stream)
+ {
+ Gdk.Pixbuf dest = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, width, height);
+ strip = new byte [strip_byte_counts];
+ int row;
+ for (int i = 0; i < offsets.Length; i++) {
+ strip = new byte [strip_byte_counts [i]];
+ stream.Read (strip, 0, strip.Length);
+ switch (compression) {
+ case Compression.Notice
+
+ }
+ }
+ }
+#endif
+ }
+#endif
+
+ public class DirectoryEntry {
+ protected TagId tagid;
+ protected EntryType type;
+ protected uint count;
+ protected uint offset_origin;
+ protected uint data_offset;
+
+ protected byte [] raw_data;
+ protected Endian endian;
+
+ public TagId Id {
+ get {
+ return tagid;
+ }
+ }
+
+ public EntryType Type {
+ get {
+ return type;
+ }
+ }
+
+ public uint Count {
+ get {
+ return count;
+ }
+ }
+
+ public virtual uint Save (OrderedWriter writer, uint position)
+ {
+#if DEBUG_LOADER
+ Console.WriteLine ("writing entry {0} {1} {2}", Id, Type, Count);
+#endif
+ writer.Write ((ushort)Id);
+ writer.Write ((ushort)Type);
+ writer.Write ((uint)Count);
+ if (Length > 4) {
+ writer.Write ((uint)position);
+ writer.Stream.Position = position;
+ writer.Stream.Write (RawData, 0, RawData.Length);
+ return (uint) (position + RawData.Length);
+ } else {
+ writer.Stream.Write (RawData, 0, RawData.Length);
+ for (int i = 0; i < 4 - RawData.Length; i++)
+ writer.Write ((byte)0);
+ }
+ return position;
+ }
+
+ public void SetOrigin (uint pos)
+ {
+ offset_origin = pos;
+ }
+
+ public uint Position {
+ get {
+ return offset_origin + data_offset;
+ }
+ }
+
+ public uint Length {
+ get { return (uint)(Count * GetTypeSize ()); }
+ }
+
+ public virtual int GetTypeSize ()
+ {
+ return GetTypeSize (type);
+ }
+
+ public static int GetTypeSize (EntryType type)
+ {
+ switch (type) {
+ case EntryType.Byte:
+ case EntryType.SByte:
+ case EntryType.Undefined:
+ case EntryType.Ascii:
+ return 1;
+ case EntryType.Short:
+ case EntryType.SShort:
+ return 2;
+ case EntryType.Long:
+ case EntryType.SLong:
+ case EntryType.Float:
+ return 4;
+ case EntryType.Double:
+ case EntryType.Rational:
+ case EntryType.SRational:
+ return 8;
+ default:
+ return 1;
+ }
+ }
+
+ public bool IsLittle {
+ get {
+ return (endian == Endian.Little);
+ }
+ }
+
+ public static int ParseHeader (byte [] data, int start, out TagId tagid, out EntryType type, Endian endian)
+ {
+ tagid = (TagId) BitConverter.ToUInt16 (data, start, endian == Endian.Little);
+ type = (EntryType) BitConverter.ToUInt16 (data, start + 2, endian == Endian.Little);
+ return 4;
+ }
+
+ public DirectoryEntry (byte [] data, int start, Endian endian)
+ {
+ this.endian = endian;
+
+ start += ParseHeader (data, start, out this.tagid, out this.type, endian);
+ ParseStream (data, start);
+ }
+
+ public virtual void LoadExternal (System.IO.Stream stream)
+ {
+ if (data_offset != 0) {
+ stream.Seek ((long)Position, System.IO.SeekOrigin.Begin);
+ byte [] data = new byte [count * GetTypeSize ()];
+ if (stream.Read (data, 0, data.Length) < data.Length) {
+#if DEBUG_LOADER
+ System.Console.WriteLine ("Short read");
+#endif
+ throw new ShortReadException ();
+ }
+ raw_data = data;
+ }
+
+#if DEBUG_LOADER
+ switch ((int)this.Id) {
+ case (int)TagId.NewSubfileType:
+ System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new NewSubFileType {0}", (NewSubfileType) this.ValueAsLong [0]);
+ break;
+ case (int)TagId.SubfileType:
+ System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new SubFileType {0}", (SubfileType) this.ValueAsLong [0]);
+ break;
+ case (int)TagId.Compression:
+ //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new Compression {0}", (Compression) this.ValueAsLong [0]);
+
+ break;
+ case (int)TagId.JPEGProc:
+ //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new JPEGProc {0}", (JPEGProc) this.ValueAsLong [0]);
+
+ break;
+ case (int)TagId.PhotometricInterpretation:
+ //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new PhotometricInterpretation {0}", (PhotometricInterpretation) this.ValueAsLong [0]);
+ break;
+ case (int)TagId.ImageWidth:
+ case (int)TagId.ImageLength:
+ //System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX new {1} {0}", this.ValueAsLong [0], this.Id);
+ break;
+ case 50648:
+ case 50656:
+ case 50752:
+ System.Console.WriteLine ("XXXXXXXXXXXXXXXXXXXXX {0}({1}) - {2} {3}", this.Id, this.Id.ToString ("x"), this.type, raw_data.Length);
+ System.Console.WriteLine ("XXXX ", System.Text.Encoding.ASCII.GetString (raw_data));
+ switch (this.type) {
+ case EntryType.Long:
+ foreach (uint val in ((LongEntry)this).Value)
+ System.Console.Write (" {0}", val);
+ break;
+ case EntryType.Short:
+ foreach (ushort val in ((ShortEntry)this).ShortValue)
+ System.Console.Write (" {0}", val);
+ break;
+ case EntryType.Byte:
+ foreach (byte val in this.RawData)
+ System.Console.Write (" {0}", val);
+ break;
+ }
+ System.Console.WriteLine (String.Empty);
+ break;
+ }
+#endif
+ }
+
+ public virtual void Dump (string name)
+ {
+ switch (this.Type) {
+ case EntryType.Short:
+ case EntryType.Long:
+ uint [] vals = this.ValueAsLong;
+ System.Console.Write ("{3}{1}({2}) [{0}] (", vals.Length, this.Id, this.Type, name);
+ for (int i = 0; i < System.Math.Min (15, vals.Length); i++) {
+ System.Console.Write (" {0}", vals [i]);
+ }
+ System.Console.WriteLine (")");
+ break;
+ case EntryType.Ascii:
+ System.Console.WriteLine ("{3}{1}({2}) (\"{0}\")", this.StringValue, this.Id, this.Type, name);
+ break;
+ default:
+ System.Console.WriteLine ("{3}{1}({2}) [{0}]", this.Count, this.Id, this.Type, name);
+ break;
+ }
+ }
+
+ protected void ParseStream (byte [] data, int start)
+ {
+ int i = start;
+
+ count = BitConverter.ToUInt32 (data, i, endian == Endian.Little);
+ i += 4;
+ int size = (int)count * GetTypeSize ();
+ if (size > 4)
+ data_offset = BitConverter.ToUInt32 (data, i, endian == Endian.Little);
+ else {
+ data_offset = 0;
+ raw_data = new byte [size];
+ System.Array.Copy (data, i, raw_data, 0, size);
+ }
+ }
+
+ public void SetData (string value)
+ {
+ int len = System.Text.Encoding.UTF8.GetByteCount (value);
+ byte [] tmp = new byte [len + 1];
+ System.Text.Encoding.UTF8.GetBytes (value, 0, value.Length, tmp, 0);
+ tmp[len] = 0;
+ System.Console.WriteLine ("SetData: value = {0} len = {1}", value, len);
+ SetData (tmp);
+ }
+
+ public static System.DateTime DateTimeFromString (string dt)
+ {
+ // Exif DateTime strings are formatted as
+ // "YYYY:MM:DD HH:MM:SS"
+
+ string delimiters = " :";
+ string[] dt_data = dt.Split ( delimiters.ToCharArray(), 6 );
+ System.DateTime result;
+ result = new System.DateTime (System.Int32.Parse(dt_data[0]),
+ System.Int32.Parse(dt_data[1]),
+ System.Int32.Parse(dt_data[2]),
+ System.Int32.Parse(dt_data[3]),
+ System.Int32.Parse(dt_data[4]),
+ System.Int32.Parse(dt_data[5]));
+
+ return result;
+ }
+
+ public void SetData (byte [] data)
+ {
+ raw_data = data;
+ count = (uint)raw_data.Length / (uint)GetTypeSize ();
+ }
+
+#if false
+ public object GetValue () {
+ switch (Type) {
+ case EntryType.Short:
+ return ShortValue;
+ case EntryType.Long:
+ return LongValue;
+ case EntryType.Rational:
+ return RationalValue;
+ case EntryType.SRational:
+ return SRationalValue;
+ case EntryType.Ascii:
+ return StringValue.Split ('\0');
+ break;
+ default:
+ System.Console.WriteLine ("{1}({2}) [{0}]", this.Count, this.Id, this.Type);
+ break;
+
+ }
+ }
+ }
+#endif
+
+ public byte [] Value {
+ get {
+ return raw_data;
+ }
+ }
+
+ public byte [] RawData {
+ get {
+ return raw_data;
+ }
+ }
+
+ public string [] ValueAsString {
+ get {
+ switch (this.Type) {
+ case EntryType.Short:
+ case EntryType.Long:
+ return ArrayToString (this.ValueAsLong);
+ case EntryType.Rational:
+ return ArrayToString (this.RationalValue);
+ case EntryType.SRational:
+ return ArrayToString (this.SRationalValue);
+ case EntryType.Undefined:
+ switch (Id) {
+ case TagId.UserComment:
+ return new string [] { UserCommentValue };
+ case TagId.FlashPixVersion:
+ case TagId.ExifVersion:
+ return new string [] { StringValue };
+ case TagId.FileSource:
+ case TagId.SceneType:
+ return ArrayToString (this.RawData);
+ case TagId.ComponentsConfiguration:
+ return ArrayToString (ValueAsLong);
+ default:
+ System.Console.WriteLine ("Cannot convert type \"{0}\" to string", Id);
+ break;
+ }
+ break;
+ case EntryType.Ascii:
+ return StringValue.Split ('\0');
+ }
+ return null;
+ }
+ }
+
+ public string [] ArrayToString (System.Array array)
+ {
+ string [] vals = new string [array.Length];
+ for (int i = 0; i < array.Length; i++)
+ vals [i] = array.GetValue (i).ToString ();
+
+ return vals;
+ }
+
+ public uint [] ValueAsLong {
+ get {
+ uint [] data = new uint [this.Count];
+ for (int i = 0; i < this.Count; i++) {
+ switch (this.Type) {
+ case EntryType.Long:
+ data [i] = BitConverter.ToUInt32 (raw_data, i * GetTypeSize (), endian == Endian.Little);
+ break;
+ case EntryType.Short:
+ data [i] = BitConverter.ToUInt16 (raw_data, i * GetTypeSize (), endian == Endian.Little);
+ break;
+ case EntryType.Undefined:
+ case EntryType.Byte:
+ data [i] = raw_data [i];
+ break;
+ default:
+ throw new ParseException ("Invalid conversion");
+ }
+ }
+ return data;
+ }
+ }
+
+ // The following methods are usded to convert the data
+ // to the various type regardless of the entry
+ // type, they are used internally in processing the data
+ // Use at your own risk.
+
+ public string StringValue {
+ get {
+ return System.Text.Encoding.ASCII.GetString (raw_data);
+ }
+ }
+
+ public System.DateTime ValueAsDate {
+ get {
+ return DirectoryEntry.DateTimeFromString (StringValue);
+ }
+ }
+
+ public string UserCommentValue {
+ get {
+ UserComment comment = new UserComment (raw_data, IsLittle);
+ return comment.Value;
+ }
+ }
+
+ public SRational [] SRationalValue {
+ get {
+ Rational [] vals = RationalValue;
+ SRational [] data = new SRational [vals.Length];
+
+ for (int i = 0; i < vals.Length; i++)
+ data [i] = SRational.BitwiseCopy (vals [i]);
+
+ return data;
+ }
+ }
+
+ public Rational [] RationalValue {
+ get {
+ uint [] vals = LongValue;
+ Rational [] data = new Rational [vals.Length / 2];
+
+ for (int i = 0; i < vals.Length; i += 2)
+ data [i/2] = new Rational (vals [i], vals [i + 1]);
+
+ return data;
+ }
+
+ }
+
+ public uint [] LongValue {
+ get {
+ uint [] data = new uint [raw_data.Length / 4];
+ for (int i = 0; i < raw_data.Length; i+= 4)
+ data [i/4] = BitConverter.ToUInt32 (raw_data, i, endian == Endian.Little);
+
+ return data;
+ }
+ }
+
+ public ushort [] ShortValue {
+ get {
+ ushort [] data = new ushort [raw_data.Length];
+ for (int i = 0; i < raw_data.Length; i+= 2) {
+ data [i] = BitConverter.ToUInt16 (raw_data, i, endian == Endian.Little);
+ }
+ return data;
+ }
+ }
+ }
+
+
+ public class TiffFile : ImageFile, SemWeb.StatementSource {
+ public Header Header;
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public TiffFile (string path) : base (path)
+ {
+ try {
+ using (System.IO.Stream input = Open ()) {
+ this.Header = new Header (input);
+ }
+
+#if DEBUG_LOADER
+ Header.Dump (this.ToString () + ":");
+#endif
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+
+ public TiffFile (Uri uri) : base (uri)
+ {
+ try {
+ using (System.IO.Stream input = Open ()) {
+ this.Header = new Header (input);
+ }
+
+#if DEBUG_LOADER
+ Header.Dump (this.ToString () + ":");
+#endif
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+
+ public virtual void Select (SemWeb.StatementSink sink)
+ {
+ Header.SelectDirectory (Header.Directory, sink);
+ }
+
+ public override System.DateTime Date {
+ get {
+ SubdirectoryEntry sub = (SubdirectoryEntry) this.Header.Directory.Lookup (TagId.ExifIfdPointer);
+ DirectoryEntry e;
+
+ if (sub != null) {
+ e = sub.Directory [0].Lookup (TagId.DateTimeOriginal);
+
+ if (e != null)
+ return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
+ }
+
+ e = this.Header.Directory.Lookup (TagId.DateTime);
+
+ if (e != null)
+ return DirectoryEntry.DateTimeFromString (e.StringValue).ToUniversalTime ();
+ else
+ return base.Date;
+ }
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ return Open ();
+ }
+
+ public override PixbufOrientation GetOrientation ()
+ {
+ ShortEntry e = (ShortEntry)(this.Header.Directory.Lookup (TagId.Orientation));
+ if (e != null)
+ return (PixbufOrientation)(e.ShortValue[0]);
+ else
+ return PixbufOrientation.TopLeft;
+ }
+
+ public System.IO.Stream LookupJpegSubstream (ImageDirectory directory)
+ {
+ uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
+
+ System.IO.Stream file = Open ();
+ file.Position = offset;
+ return file;
+ }
+
+#if false
+ public Gdk.Pixbuf LoadJpegInterchangeFormat (ImageDirectory directory)
+ {
+ uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
+ uint length = directory.Lookup (TagId.JPEGInterchangeFormatLength).ValueAsLong [0];
+
+ using (System.IO.Stream file = Open ()) {
+ file.Position = offset;
+
+ byte [] data = new byte [32768];
+ int read;
+
+ Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
+
+ while (length > 0) {
+ read = file.Read (data, 0, (int)System.Math.Min ((int)data.Length, length));
+ if (read <= 0)
+ break;
+
+ loader.Write (data, (ulong)read);
+ length -= (uint) read;
+ }
+ Gdk.Pixbuf result = loader.Pixbuf;
+ loader.Close ();
+ return result;
+ }
+ }
+#endif
+ }
+
+ public class DngFile : TiffFile {
+ public DngFile (string path) : base (path)
+ {
+ }
+
+ public DngFile (System.Uri uri) : base (uri)
+ {
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ try {
+ SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
+ ImageDirectory directory = sub.Directory [sub.Directory.Length - 1];
+
+ uint offset = directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
+ System.IO.Stream file = Open ();
+ file.Position = offset;
+ return file;
+ } catch {
+ return DCRawFile.RawPixbufStream (uri);
+ }
+ }
+
+ public override void Select (SemWeb.StatementSink sink)
+ {
+
+ /* this is just a sanity pass, if the first ifd is not a subfile use the normal
+ * tiff path
+ */
+ DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
+ if (e == null) {
+ base.Select (sink);
+ return;
+ }
+
+ /*
+ * Even though Ifd0 doesn't have the full resolution image
+ * it would have the XMP data so we look for it
+ */
+ e = Header.Directory.Lookup (TagId.XMP);
+ if (e != null) {
+ System.IO.Stream xmpstream = new System.IO.MemoryStream (e.RawData);
+ FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
+ xmp.Select (sink);
+ }
+
+ /*
+ * Ifd0 will also have the exif directory
+ */
+ ImageDirectory dir = Header.Directory;
+ SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
+ if (sub != null)
+ Header.SelectDirectory (sub.Directory [0], sink);
+
+ /*
+ * now we lookup subifd0 (we should probably scan the newsubfile types here)
+ * and load the metadata we are interested in from it.
+ */
+ sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
+
+ if (e == null)
+ e = dir.Lookup (TagId.NewSubfileType);
+
+ int i = 0;
+ do {
+ uint dirtype = e.ValueAsLong [0];
+ if (dirtype == 0) {
+ Header.SelectDirectory (dir, sink);
+ break;
+ }
+
+ if (sub == null)
+ break;
+
+ dir = sub.Directory [i];
+ e = dir.Lookup (TagId.NewSubfileType);
+ i++;
+ } while (i < sub.Directory.Length);
+
+
+ }
+ }
+
+ public class NefFile : TiffFile, IThumbnailContainer {
+ public NefFile (string path) : base (path)
+ {
+ }
+
+ public NefFile (Uri uri) : base (uri)
+ {
+ }
+
+ public override void Select (SemWeb.StatementSink sink)
+ {
+ DirectoryEntry e = Header.Directory.Lookup (TagId.NewSubfileType);
+
+ if (e == null) {
+ base.Select (sink);
+ return;
+ }
+
+ ImageDirectory dir = Header.Directory;
+ SubdirectoryEntry sub = (SubdirectoryEntry) dir.Lookup (TagId.ExifIfdPointer);
+
+ if (sub != null)
+ Header.SelectDirectory (sub.Directory [0], sink);
+
+ sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
+
+ int i = 0;
+ do {
+ uint dirtype = e.ValueAsLong [0];
+ if (dirtype == 0) {
+ Header.SelectDirectory (dir, sink);
+ break;
+ }
+
+ if (sub == null)
+ break;
+
+ dir = sub.Directory [i];
+ if (dir != null)
+ e = dir.Lookup (TagId.NewSubfileType);
+ i++;
+ } while (i < sub.Directory.Length);
+ }
+
+#if false
+ public Gdk.Pixbuf GetEmbeddedThumbnail ()
+ {
+ using (System.IO.Stream stream = Open ()) {
+ return TransformAndDispose (new Gdk.Pixbuf (stream));
+ }
+ }
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ try {
+ SubdirectoryEntry sub = (SubdirectoryEntry) Header.Directory.Lookup (TagId.SubIFDs);
+ ImageDirectory jpeg_directory = sub.Directory [0];
+ return LookupJpegSubstream (jpeg_directory);
+ } catch (System.Exception) {
+ return DCRawFile.RawPixbufStream (uri);
+ }
+ }
+#endif
+ }
+
+
+ public class Cr2File : TiffFile, IThumbnailContainer {
+ public Cr2File (string path) : base (path)
+ {
+ }
+
+ public Cr2File (Uri uri) : base (uri)
+ {
+ }
+
+ /*
+ public override PixbufOrientation GetOrientation ()
+ {
+ return PixbufOrientation.TopLeft;
+ }
+ */
+
+#if false
+ public Gdk.Pixbuf GetEmbeddedThumbnail ()
+ {
+ ImageDirectory directory;
+ directory = Header.Directory.NextDirectory;
+ return TransformAndDispose (LoadJpegInterchangeFormat (directory));
+ }
+#endif
+
+ public override System.IO.Stream PixbufStream ()
+ {
+ uint offset = Header.Directory.Lookup (TagId.StripOffsets).ValueAsLong [0];
+ System.IO.Stream file = Open ();
+ file.Position = offset;
+ return file;
+ }
+ }
+
+#if ENABLE_NUNIT
+ [TestFixture]
+ public class Tests {
+ public Tests ()
+ {
+ Gnome.Vfs.Vfs.Initialize ();
+ Gtk.Application.Init ();
+ }
+
+ [Test]
+ public void Save ()
+ {
+ string desc = "this is an example description";
+ string desc2 = "\x00a9 Novell Inc.";
+ PixbufOrientation orient = PixbufOrientation.TopRight;
+ Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
+ string path = ImageFile.TempPath ("joe.jpg");
+
+ PixbufUtils.SaveJpeg (test, path, 75, new Exif.ExifData ());
+ JpegFile jimg = new JpegFile (path);
+ jimg.SetDescription (desc);
+ jimg.SetOrientation (orient);
+ jimg.SaveMetaData (path);
+ JpegFile mod = new JpegFile (path);
+ Assert.AreEqual (mod.Orientation, orient);
+ Assert.AreEqual (mod.Description, desc);
+ jimg.SetDescription (desc2);
+ jimg.SaveMetaData (path);
+ mod = new JpegFile (path);
+ Assert.AreEqual (mod.Description, desc2);
+
+ Header header = mod.ExifHeader;
+#if USE_TEST_FILE
+ string tmp = "/home/lewing/test.tiff";
+ if (File.Exists (tmp))
+ File.Delete (tmp);
+ Stream stream = File.Open (tmp, FileMode.Create, FileAccess.ReadWrite);
+ Console.WriteLine ("XXXX saving tiff {0}", tmp);
+#else
+ System.IO.MemoryStream stream = new System.IO.MemoryStream ();
+#endif
+
+ header.Dump ("source");
+ header.Save (stream);
+ stream.Position = 0;
+ System.Console.WriteLine ("----------------------------------------------LOADING TIFF");
+ Header loader = new Header (stream);
+ loader.Dump ("loader");
+
+ CompareDirectories (header.Directory, loader.Directory);
+
+ System.IO.File.Delete (path);
+ }
+
+ private void CompareDirectories (ImageDirectory olddir, ImageDirectory newdir)
+ {
+ Assert.AreEqual (olddir.Entries.Count, newdir.Entries.Count);
+ for (int i = 0; i < olddir.Entries.Count; i++) {
+ Assert.AreEqual (olddir.Entries [i].Id, newdir.Entries [i].Id);
+ Assert.AreEqual (olddir.Entries [i].Type, newdir.Entries [i].Type);
+ Assert.AreEqual (olddir.Entries [i].Count, newdir.Entries [i].Count);
+ Assert.AreEqual (olddir.Entries [i].Length, newdir.Entries [i].Length);
+ if (olddir.Entries [i] is SubdirectoryEntry) {
+ SubdirectoryEntry oldsub = olddir.Entries [i] as SubdirectoryEntry;
+ SubdirectoryEntry newsub = newdir.Entries [i] as SubdirectoryEntry;
+
+ for (int j = 0; j < oldsub.Directory.Length; j++)
+ CompareDirectories (oldsub.Directory [j], newsub.Directory [j]);
+ }
+ }
+ }
+ }
+#endif
+}
+
Added: trunk/beagle/Util/F-Spot/Imaging/X3fFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/X3fFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,112 @@
+using System;
+using System.IO;
+using FSpot;
+using SemWeb;
+
+namespace FSpot.X3f {
+ internal class Info {
+ ushort major_version;
+ ushort minor_version;
+ byte [] uid;
+ uint mark_bits;
+ uint cols;
+ uint rows;
+ uint rotation;
+
+ public PixbufOrientation Orientation {
+ get {
+ switch (rotation) {
+ case 0:
+ return PixbufOrientation.TopLeft;
+ case 270:
+ return PixbufOrientation.LeftBottom;
+ case 180:
+ return PixbufOrientation.BottomRight;
+ case 90:
+ return PixbufOrientation.RightTop;
+ default:
+ return PixbufOrientation.TopLeft;
+ }
+ }
+ }
+
+ public uint Width {
+ get { return cols; }
+ }
+
+ public uint Height {
+ get { return rows; }
+ }
+
+ public Info (Stream stream)
+ {
+ byte [] buffer = new byte [4];
+ stream.Read (buffer, 0, buffer.Length);
+ if (System.Text.Encoding.ASCII.GetString (buffer, 0, buffer.Length) != "FOVb")
+ throw new ImageFormatException ("invalid Foveon magic number");
+
+ stream.Read (buffer, 0, buffer.Length);
+ major_version = BitConverter.ToUInt16 (buffer, 0, true);
+ minor_version = BitConverter.ToUInt16 (buffer, 2, true);
+
+ uid = new byte [16];
+ stream.Read (uid, 0, uid.Length);
+
+ stream.Read (buffer, 0, buffer.Length);
+ mark_bits = BitConverter.ToUInt32 (buffer, 0, true);
+ stream.Read (buffer, 0, buffer.Length);
+ cols = BitConverter.ToUInt32 (buffer, 0, true);
+ stream.Read (buffer, 0, buffer.Length);
+ rows = BitConverter.ToUInt32 (buffer, 0, true);
+ stream.Read (buffer, 0, buffer.Length);
+ rotation = BitConverter.ToUInt32 (buffer, 0, true);
+ }
+
+ public override string ToString ()
+ {
+ return String.Format ("x3file ({0}.{1}, mark_bits={2}) ({3}, {4})",
+ major_version, minor_version,
+ mark_bits, Width, Height);
+ }
+
+ }
+
+ public class X3fFile : DCRawFile, SemWeb.StatementSource {
+ Info info;
+
+ // false seems a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ internal Info Info {
+ get {
+ if (info == null)
+ info = new Info (Open ());
+
+ return info;
+ }
+ }
+
+ public X3fFile (string path) : base (path)
+ {
+ }
+
+ public X3fFile (System.Uri uri) : base (uri)
+ {
+ }
+
+ public void Select (StatementSink sink)
+ {
+ MetadataStore.AddLiteral (sink, "tiff:Orientation", ((int)Info.Orientation).ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageWidth", Info.Width.ToString ());
+ MetadataStore.AddLiteral (sink, "tiff:ImageLength", Info.Height.ToString ());
+ }
+
+ public override PixbufOrientation GetOrientation ()
+ {
+ return Info.Orientation;
+ }
+
+ }
+}
Added: trunk/beagle/Util/F-Spot/Imaging/XmpFile.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/Imaging/XmpFile.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,129 @@
+using System.Xml;
+using System.Collections;
+using SemWeb;
+
+namespace FSpot.Xmp {
+ public class XmpFile : SemWeb.StatementSource, SemWeb.StatementSink
+ {
+ MetadataStore store;
+
+ // false seems like a safe default
+ public bool Distinct {
+ get { return false; }
+ }
+
+ public MetadataStore Store {
+ get { return store; }
+ set { store = value; }
+ }
+
+ public XmpFile (System.IO.Stream stream) : this ()
+ {
+ Load (stream);
+ }
+
+ public XmpFile ()
+ {
+ store = new MetadataStore ();
+ }
+
+ public void Load (System.IO.Stream stream)
+ {
+ try {
+ RdfXmlReader reader = new RdfXmlReader (stream);
+ reader.BaseUri = MetadataStore.FSpotXMPBase;
+ store.Import (reader);
+ //Dump ();
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e.ToString ());
+ }
+ }
+
+ private class XmpWriter : RdfXmlWriter {
+ public XmpWriter (XmlDocument dest) : base (dest)
+ {
+ BaseUri = MetadataStore.FSpotXMPBase;
+ }
+
+ public override void Add (Statement stmt)
+ {
+ string predicate = stmt.Predicate.Uri;
+ string prefix;
+ string localname;
+
+ // Fill in the namespaces with nice prefixes
+ if (MetadataStore.Namespaces.Normalize (predicate, out prefix, out localname)) {
+ if (prefix != null)
+ Namespaces.AddNamespace (predicate.Remove (predicate.Length - localname.Length, localname.Length), prefix);
+ }
+ base.Add (stmt);
+ }
+ }
+
+ public void Save (System.IO.Stream stream)
+ {
+ try {
+ XmlTextWriter text;
+ RdfXmlWriter writer;
+ XmlDocument rdfdoc = new XmlDocument();
+
+ // first, construct the rdf guts, semweb style
+ writer = new XmpWriter (rdfdoc);
+ //writer.Namespaces.Parent = MetadataStore.Namespaces;
+ writer.Write (store);
+ writer.Close ();
+
+ // now construct the xmp wrapper packet
+ text = new XmlTextWriter (stream, System.Text.Encoding.UTF8);
+ text.Formatting = Formatting.Indented;
+
+ text.WriteProcessingInstruction ("xpacket", "begin=\"\ufeff\" id=\"W5M0MpCehiHzreSzNTczkc9d\"");
+ text.WriteStartElement ("x:xmpmeta");
+ text.WriteAttributeString ("xmlns", "x", null, "adobe:ns:meta/");
+
+ ((XmlElement)rdfdoc.ChildNodes[1]).RemoveAttribute ("xml:base");
+ rdfdoc.ChildNodes[1].WriteTo (text);
+
+ // now close off the xmp packet
+ text.WriteEndElement ();
+ text.WriteProcessingInstruction ("xpacket", "end=\"r\"");
+ text.Close ();
+
+ } catch (System.Exception e) {
+ System.Console.WriteLine (e);
+ }
+ }
+
+ public bool Add (Statement stmt)
+ {
+ return ((SemWeb.StatementSink)store).Add (stmt);
+ }
+
+ public void Select (SemWeb.StatementSink sink)
+ {
+ store.Select (sink);
+ }
+
+ public void Dump ()
+ {
+ foreach (SemWeb.Statement stmt in store) {
+ System.Console.WriteLine(stmt);
+ }
+ }
+
+#if TEST_XMP
+ static void Main (string [] args)
+ {
+ XmpFile xmp = new XmpFile (System.IO.File.OpenRead (args [0]));
+ //xmp.Store.Dump ();
+#if false
+ System.IO.StreamReader stream = new System.IO.StreamReader (System.IO.File.OpenRead (args [0]));
+
+ while (stream.BaseStream.Position < stream.BaseStream.Length) {
+ System.Console.WriteLine (stream.ReadLine ());
+ }
+#endif
+ }
+#endif
+ }
+}
Added: trunk/beagle/Util/F-Spot/MetadataStore.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/MetadataStore.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,361 @@
+using SemWeb;
+using SemWeb.Util;
+using Mono.Unix;
+using FSpot.Xmp;
+
+namespace FSpot {
+ internal class Description {
+ string predicate;
+ string description;
+ string title;
+ ValueFormat formater;
+
+ static System.Collections.Hashtable table;
+
+ static Description ()
+ {
+ Description [] preset = new Description [] {
+ new Description ("dc:creator", Catalog.GetString ("Creator")),
+ new Description ("dc:title", Catalog.GetString ("Title")),
+ new Description ("dc:rights", Catalog.GetString ("Copyright")),
+ new Description ("dc:subject", Catalog.GetString ("Subject and Keywords")),
+ new Description ("tiff:Compression", Catalog.GetString ("Compression"),
+ typeof (FSpot.Tiff.Compression)),
+ new Description ("tiff:PlanarConfiguration", Catalog.GetString ("Planar Configuration"),
+ typeof (FSpot.Tiff.PlanarConfiguration)),
+ new Description ("tiff:Orientation", Catalog.GetString ("Orientation"),
+ typeof (PixbufOrientation)),
+ new Description ("tiff:PhotometricInterpretation", Catalog.GetString ("Photometric Interpretation"),
+ typeof (FSpot.Tiff.PhotometricInterpretation)),
+ new Description ("tiff:ResolutionUnit", Catalog.GetString ("Resolution Unit"),
+ typeof (FSpot.Tiff.ResolutionUnit)),
+ new Description ("exif:ExposureProgram", Catalog.GetString ("Exposure Program"),
+ typeof (FSpot.Tiff.ExposureProgram)),
+ new Description ("exif:MeteringMode", Catalog.GetString ("Metering Mode"),
+ typeof (FSpot.Tiff.MeteringMode)),
+ new Description ("exif:ExposureMode", Catalog.GetString ("Exposure Mode"),
+ typeof (FSpot.Tiff.ExposureMode)),
+ new Description ("exif:CustomRendered", Catalog.GetString ("Custom Rendered"),
+ typeof (FSpot.Tiff.CustomRendered)),
+ new Description ("exif:ComponentsConfiguration", Catalog.GetString ("Components Configuration"),
+ typeof (FSpot.Tiff.ComponentsConfiguration)),
+ new Description ("exif:LightSource", Catalog.GetString ("Light Source"),
+ typeof (FSpot.Tiff.LightSource)),
+ new Description ("exif:SensingMethod", Catalog.GetString ("Sensing Method"),
+ typeof (FSpot.Tiff.SensingMethod)),
+ new Description ("exif:ColorSpace", Catalog.GetString ("Color Space"),
+ typeof (FSpot.Tiff.ColorSpace)),
+ new Description ("exif:WhiteBalance", Catalog.GetString ("White Balance"),
+ typeof (FSpot.Tiff.WhiteBalance)),
+ new Description ("exif:FocalPlaneResolutionUnit", Catalog.GetString ("Focal Plane Resolution Unit"),
+ typeof (FSpot.Tiff.ResolutionUnit)),
+ new Description ("exif:FileSource", Catalog.GetString ("File Source Type"),
+ typeof (FSpot.Tiff.FileSource)),
+ new Description ("exif:SceneCaptureType", Catalog.GetString ("Scene Capture Type"),
+ typeof (FSpot.Tiff.SceneCaptureType)),
+ new Description ("exif:GainControl", Catalog.GetString ("Gain Control"),
+ typeof (FSpot.Tiff.GainControl)),
+ new Description ("exif:Contrast", Catalog.GetString ("Contrast"),
+ typeof (FSpot.Tiff.Contrast)),
+ new Description ("exif:Saturation", Catalog.GetString ("Saturation"),
+ typeof (FSpot.Tiff.Saturation)),
+ new Description ("exif:Sharpness", Catalog.GetString ("Sharpness"),
+ typeof (FSpot.Tiff.Sharpness)),
+ new Description ("exif:SceneType", Catalog.GetString ("Scene Type"),
+ typeof (FSpot.Tiff.SceneType))
+
+
+
+ };
+
+ table = new System.Collections.Hashtable ();
+
+ foreach (Description d in preset) {
+ table [MetadataStore.Namespaces.Resolve (d.predicate)] = d;
+ }
+ }
+
+ public Description (string predicate, string title) : this (predicate, title, null, null) {}
+
+ public Description (string predicate, string title, string description) : this (predicate, title, description, null) {}
+
+ public Description (string predicate, string title, System.Type type) : this (predicate, title)
+ {
+ formater = new ValueFormat (type);
+ }
+
+ public Description (string predicate, string title, string description, ValueFormat formater)
+ {
+ this.predicate = predicate;
+ this.title = title;
+ this.formater = formater;
+ }
+
+ public static void GetDescription (MemoryStore store, Statement stmt, out string label, out string value)
+ {
+ string predicate = stmt.Predicate.Uri;
+
+ Description d = (Description) table [predicate];
+
+ label = System.IO.Path.GetFileName (predicate);
+ value = null;
+
+ if (stmt.Object is SemWeb.Literal)
+ value = ((SemWeb.Literal)(stmt.Object)).Value;
+
+ if (d != null) {
+ label = d.title;
+ if (d.formater != null && stmt.Object is Literal)
+ value = d.formater.GetValue (store, (SemWeb.Literal)stmt.Object);
+
+ } else {
+ Statement sstmt = new Statement (stmt.Predicate,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdfs:label"),
+ null);
+
+ foreach (Statement tstmt in MetadataStore.Descriptions.Select (sstmt))
+ if (tstmt.Object is SemWeb.Literal)
+ label = ((SemWeb.Literal)(tstmt.Object)).Value;
+ }
+ return;
+ }
+ }
+
+ internal class ValueFormat
+ {
+ System.Type type;
+
+ public ValueFormat (System.Type type)
+ {
+ this.type = type;
+ }
+
+ public virtual string GetValue (MemoryStore store, SemWeb.Literal obj)
+ {
+ string result = obj.Value;
+
+ if (type.IsEnum) {
+ try {
+ object o = System.Enum.Parse (type, obj.Value);
+ result = o.ToString ();
+ } catch (System.Exception e) {
+ System.Console.WriteLine ("Value \"{2}\" not found in {0}{3}{1}", type, e, result, System.Environment.NewLine);
+ }
+ }
+ /*
+ else if (type == typeof (Rational)) {
+ object o = FSpot.Tiff.Rational.Parse (obj.Value);
+ }
+ */
+ return result;
+ }
+ }
+
+ public class MetadataStore : MemoryStore
+ {
+ public static NamespaceManager Namespaces;
+ private static MetadataStore descriptions;
+
+ public const string PhotoshopNS = "http://ns.adobe.com/photoshop/1.0/";
+ public const string Iptc4xmpCoreNS = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
+ public const string DcNS = "http://purl.org/dc/elements/1.1/";
+ public const string XmpNS = "http://ns.adobe.com/xap/1.0/";
+ public const string XmpidqNS = "http://ns.adobe.com/xmp/Identifier/qual/1.0/";
+ public const string XmpRightsNS = "http://ns.adobe.com/xap/1.0/rights/";
+ public const string XmpBJNS = "http://ns.adobe.com/xap/1.0/bj/";
+ public const string XmpMMNS = "http://ns.adobe.com/xap/1.0/mm/";
+ public const string ExifNS = "http://ns.adobe.com/exif/1.0/";
+ public const string TiffNS = "http://ns.adobe.com/tiff/1.0/";
+ public const string RdfNS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+ public const string RdfsNS = "http://www.w3.org/2000/01/rdf-schema#";
+ public const string IViewNS = "http://ns.iview-multimedia.com/mediapro/1.0/";
+ public const string XmlNS = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
+
+ // FIXME this needs to be parsable by System.Uri
+ public const string FSpotXMPBase = "http://fakebase.f-spot.org/internal/";
+
+ static MetadataStore ()
+ {
+ Namespaces = new NamespaceManager ();
+
+ Namespaces.AddNamespace (PhotoshopNS, "photoshop");
+ Namespaces.AddNamespace (Iptc4xmpCoreNS, "Iptc4xmpCore");
+ Namespaces.AddNamespace (DcNS, "dc");
+ Namespaces.AddNamespace (XmpNS, "xmp");
+ Namespaces.AddNamespace (XmpidqNS, "xmpidq");
+ Namespaces.AddNamespace (XmpRightsNS, "xmpRights");
+ Namespaces.AddNamespace (XmpBJNS, "xmpBJ");
+ Namespaces.AddNamespace (XmpMMNS, "xmpMM");
+ Namespaces.AddNamespace (ExifNS, "exif");
+ Namespaces.AddNamespace (TiffNS, "tiff");
+ Namespaces.AddNamespace (RdfNS, "rdf");
+ Namespaces.AddNamespace (RdfsNS, "rdfs");
+ Namespaces.AddNamespace (IViewNS, "mediapro");
+ }
+
+ public static MetadataStore Descriptions {
+ get {
+ if (descriptions == null) {
+ descriptions = new MetadataStore ();
+ System.IO.Stream stream = System.Reflection.Assembly.GetCallingAssembly ().GetManifestResourceStream ("dces.rdf");
+ if (stream != null) {
+ descriptions.Import (new RdfXmlReader (stream));
+ } else {
+ System.Console.WriteLine ("Can't find resource");
+ }
+ }
+
+ return descriptions;
+ }
+ }
+
+ public void Dump ()
+ {
+#if enable_debug
+ XmpFile xmp = new XmpFile ();
+ xmp.Store = this;
+ xmp.Save (System.Console.OpenStandardOutput ());
+#endif
+ }
+
+ public static void AddLiteral (StatementSink sink, string predicate, string type, SemWeb.Literal value)
+ {
+ Entity empty = new BNode ();
+ Statement top = new Statement (FSpotXMPBase, (Entity)MetadataStore.Namespaces.Resolve (predicate), empty);
+ Statement desc = new Statement (empty,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:type"),
+ (Entity)MetadataStore.Namespaces.Resolve (type));
+ sink.Add (desc);
+ Statement literal = new Statement (empty,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:li"),
+ value);
+ sink.Add (literal);
+ sink.Add (top);
+ }
+
+ public static void AddLiteral (StatementSink sink, string predicate, string value)
+ {
+ Statement stmt = new Statement (FSpotXMPBase,
+ (Entity)MetadataStore.Namespaces.Resolve (predicate),
+ new SemWeb.Literal (value));
+ sink.Add (stmt);
+ }
+
+ public static void Add (StatementSink sink, string predicate, string type, string [] values)
+ {
+ Add (sink, FSpotXMPBase, predicate, type, values);
+ }
+
+ public void Delete (string predicate)
+ {
+ System.Collections.ArrayList to_remove = new System.Collections.ArrayList ();
+ foreach (Statement stmt in this) {
+ if (stmt.Predicate == MetadataStore.Namespaces.Resolve (predicate)) {
+ to_remove.Add (stmt);
+ break;
+ }
+ }
+
+ foreach (Statement stmt in to_remove)
+ this.Remove (stmt);
+ }
+
+ public void Update (string predicate, string value1)
+ {
+ // Delete first..
+ Delete (predicate);
+ // Add after...
+ AddLiteral (this, predicate, value1);
+ }
+
+ public void Update (string predicate, string type, string [] values)
+ {
+ Entity anon = null;
+
+ System.Collections.ArrayList to_remove = new System.Collections.ArrayList ();
+ foreach (Statement stmt in this) {
+ if (stmt.Predicate == MetadataStore.Namespaces.Resolve (predicate)) {
+ if (type != null) // only look further if we have a type.
+ anon = (Entity) stmt.Object;
+ to_remove.Add (stmt);
+ break;
+ }
+ }
+
+ if (anon != null) {
+ foreach (Statement stmt in this) {
+ if (stmt.Subject == anon) {
+ to_remove.Add (stmt);
+ }
+ }
+ }
+
+ foreach (Statement stmt in to_remove)
+ this.Remove (stmt);
+
+ if (values.Length > 0) {
+ Add (this, predicate, type, values);
+ }
+ }
+
+ public static void Add (StatementSink sink, Entity subject, string predicate, string type, string [] values)
+ {
+ if (values == null) {
+ System.Console.WriteLine ("{0} has no values; skipping", predicate);
+ return;
+ }
+
+ Entity empty = new SemWeb.BNode();
+ Statement top = new Statement (subject, (Entity)MetadataStore.Namespaces.Resolve (predicate), empty);
+ Statement desc = new Statement (empty,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:type"),
+ (Entity)MetadataStore.Namespaces.Resolve (type));
+ sink.Add (desc);
+ foreach (string value in values) {
+ Statement literal = new Statement (empty,
+ (Entity)MetadataStore.Namespaces.Resolve ("rdf:li"),
+ new SemWeb.Literal (value, null, null));
+ sink.Add (literal);
+ }
+ sink.Add (top);
+ }
+
+ private class StatementWriter : StatementSink
+ {
+ string name;
+ public StatementWriter (string name)
+ {
+ this.name = name;
+ }
+
+ public bool Add (Statement stmt)
+ {
+ string predicate = stmt.Predicate.ToString ();
+
+ if (predicate.StartsWith (name))
+ System.Console.WriteLine ("----------- {0}", stmt);
+
+ return true;
+ }
+ }
+
+ private class SelectFirst : StatementSink
+ {
+ public Statement Statement;
+
+ public bool Add (Statement stmt)
+ {
+ this.Statement = stmt;
+ return false;
+ }
+ }
+
+ public void DumpNode (XPathSemWebNavigator navi, int depth)
+ {
+ do {
+ System.Console.WriteLine ("node [{0}] {1} {2}", depth, navi.Name, navi.Value);
+ } while (navi.MoveToNext ());
+ }
+
+ }
+}
Added: trunk/beagle/Util/F-Spot/PixbufUtils.cs
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/PixbufUtils.cs Sun Apr 13 20:18:35 2008
@@ -0,0 +1,944 @@
+using System.Collections;
+using System.Runtime.InteropServices;
+using System;
+using System.IO;
+using FSpot;
+
+/**
+ 1 2 3 4 5 6 7 8
+
+888888 888888 88 88 8888888888 88 88 8888888888
+88 88 88 88 88 88 88 88 88 88 88 88
+8888 8888 8888 8888 88 8888888888 8888888888 88
+88 88 88 88
+88 88 888888 888888
+
+t-l t-r b-r b-l l-t r-t r-b l-b
+
+**/
+
+public enum PixbufOrientation {
+ TopLeft = 1,
+ TopRight = 2,
+ BottomRight = 3,
+ BottomLeft = 4,
+ LeftTop = 5,
+ RightTop = 6,
+ RightBottom = 7,
+ LeftBottom = 8
+}
+
+public class PixbufUtils {
+
+ static public PixbufOrientation Rotate270 (PixbufOrientation orientation)
+ {
+ PixbufOrientation [] rot = new PixbufOrientation [] {
+ PixbufOrientation.LeftBottom,
+ PixbufOrientation.LeftTop,
+ PixbufOrientation.RightTop,
+ PixbufOrientation.RightBottom,
+ PixbufOrientation.BottomLeft,
+ PixbufOrientation.TopLeft,
+ PixbufOrientation.TopRight,
+ PixbufOrientation.BottomRight
+ };
+
+ orientation = rot [((int)orientation) -1];
+ return orientation;
+ }
+
+ static public PixbufOrientation Rotate90 (PixbufOrientation orientation)
+ {
+ orientation = Rotate270 (orientation);
+ orientation = Rotate270 (orientation);
+ orientation = Rotate270 (orientation);
+ return orientation;
+ }
+
+#if false
+ static Pixbuf error_pixbuf = null;
+ public static Pixbuf ErrorPixbuf {
+ get {
+ if (error_pixbuf == null)
+ error_pixbuf = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "f-spot-question-mark", 256, (Gtk.IconLookupFlags)0);
+ return error_pixbuf;
+ }
+ }
+ public static Pixbuf LoadingPixbuf = PixbufUtils.LoadFromAssembly ("f-spot-loading.png");
+
+ public static int GetSize (Pixbuf pixbuf)
+ {
+ return Math.Max (pixbuf.Width, pixbuf.Height);
+ }
+
+ public static double Fit (Pixbuf pixbuf,
+ int dest_width, int dest_height,
+ bool upscale_smaller,
+ out int fit_width, out int fit_height)
+ {
+ return Fit (pixbuf.Width, pixbuf.Height,
+ dest_width, dest_height,
+ upscale_smaller,
+ out fit_width, out fit_height);
+ }
+
+ public static double Fit (int orig_width, int orig_height,
+ int dest_width, int dest_height,
+ bool upscale_smaller,
+ out int fit_width, out int fit_height)
+ {
+ if (orig_width == 0 || orig_height == 0) {
+ fit_width = 0;
+ fit_height = 0;
+ return 0.0;
+ }
+
+ double scale = Math.Min (dest_width / (double)orig_width,
+ dest_height / (double)orig_height);
+
+ if (scale > 1.0 && !upscale_smaller)
+ scale = 1.0;
+
+ fit_width = (int)(scale * orig_width);
+ fit_height = (int)(scale * orig_height);
+
+ return scale;
+ }
+
+
+ // FIXME: These should be in GTK#. When my patch is committed, these LoadFrom* methods will
+ // go away.
+
+ public class AspectLoader {
+ Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
+ int max_width;
+ int max_height;
+ PixbufOrientation orientation;
+ int orig_width;
+
+ public AspectLoader (int max_width, int max_height)
+ {
+ this.max_height = max_height;
+ this.max_width = max_width;
+ loader.SizePrepared += HandleSizePrepared;
+ }
+
+ private void HandleSizePrepared (object obj, SizePreparedArgs args)
+ {
+ switch (orientation) {
+ case PixbufOrientation.LeftTop:
+ case PixbufOrientation.LeftBottom:
+ case PixbufOrientation.RightTop:
+ case PixbufOrientation.RightBottom:
+ int tmp = max_width;
+ max_width = max_height;
+ max_height = tmp;
+ break;
+ default:
+ break;
+ }
+
+ double scale = Math.Min (max_width / (double)args.Width,
+ max_height / (double)args.Height);
+
+
+ int scale_width = (int)(scale * args.Width);
+ int scale_height = (int)(scale * args.Height);
+
+ if (scale < 1.0)
+ loader.SetSize (scale_width, scale_height);
+ }
+
+ public Pixbuf Load (System.IO.Stream stream, PixbufOrientation orientation)
+ {
+ int count;
+ byte [] data = new byte [8192];
+ while (((count = stream.Read (data, 0, data.Length)) > 0) && loader.Write (data, (ulong)count))
+ ;
+
+ loader.Close ();
+ Pixbuf orig = loader.Pixbuf;
+ Gdk.Pixbuf rotated = TransformOrientation (orig, orientation, true);
+
+ if (orig != rotated) {
+ CopyThumbnailOptions (orig, rotated);
+ orig.Dispose ();
+ }
+ loader.Dispose ();
+ return rotated;
+ }
+
+ public Pixbuf LoadFromFile (string path)
+ {
+ try {
+ orientation = GetOrientation (path);
+ using (FileStream fs = File.OpenRead (path)) {
+ return Load (fs, orientation);
+ }
+ } catch (Exception) {
+ System.Console.WriteLine ("Error loading photo {0}", path);
+ return null;
+ }
+ }
+ }
+
+ public static Pixbuf ShallowCopy (Pixbuf pixbuf)
+ {
+ Pixbuf result = new Pixbuf (pixbuf, 0, 0, pixbuf.Width, pixbuf.Height);
+ CopyThumbnailOptions (pixbuf, result);
+ return result;
+ }
+
+ public static Pixbuf ScaleToMaxSize (Pixbuf pixbuf, int width, int height)
+ {
+ return ScaleToMaxSize (pixbuf, width, height, true);
+ }
+
+ public static Pixbuf ScaleToMaxSize (Pixbuf pixbuf, int width, int height, bool upscale)
+ {
+ double scale = Math.Min (width / (double)pixbuf.Width, height / (double)pixbuf.Height);
+ int scale_width = (int)(scale * pixbuf.Width);
+ int scale_height = (int)(scale * pixbuf.Height);
+
+ Gdk.Pixbuf result;
+ if (upscale || (scale < 1.0))
+ result = pixbuf.ScaleSimple (scale_width, scale_height, (scale_width > 20) ? Gdk.InterpType.Bilinear : Gdk.InterpType.Nearest);
+ else
+ result = pixbuf.Copy ();
+
+ CopyThumbnailOptions (pixbuf, result);
+
+ return result;
+ }
+
+ static public void GetSize (string path, out int width, out int height)
+ {
+ Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
+ int orig_width = 0;
+ int orig_height = 0;
+ bool done = false;
+
+ loader.SizePrepared += delegate (object obj, SizePreparedArgs args) {
+ orig_width = args.Width;
+ orig_height = args.Height;
+ done = true;
+ };
+
+ using (Stream stream = File.OpenRead (path)) {
+ byte [] data = new byte [4096];
+ int count;
+
+ while (((count = stream.Read (data, 0, data.Length)) > 0) && loader.Write (data, (ulong)count)) {
+ if (done)
+ break;
+ }
+ }
+
+ width = orig_width;
+ height = orig_height;
+ }
+
+ static public Pixbuf LoadAtMaxSize (string path, int max_width, int max_height)
+ {
+#if true
+ PixbufUtils.AspectLoader loader = new AspectLoader (max_width, max_height);
+ return loader.LoadFromFile (path);
+#else
+ int width, height;
+ JpegUtils.GetSize (path, out width, out height);
+ PixbufUtils.Fit (width, height, max_width, max_height, false, out width, out height);
+ Gdk.Pixbuf image = JpegUtils.LoadScaled (path, width, height);
+
+ return image;
+#endif
+ }
+
+ static public Pixbuf LoadFromStream (System.IO.Stream input)
+ {
+ Gdk.PixbufLoader loader = new Gdk.PixbufLoader ();
+ byte [] buffer = new byte [8192];
+ int n;
+
+ while ((n = input.Read (buffer, 0, 8192)) != 0)
+ loader.Write (buffer, (ulong) n);
+
+ loader.Close ();
+
+ return loader.Pixbuf;
+
+ }
+
+
+ //
+ // FIXME this is actually not public api and we should do a verison check,
+ // but frankly I'm irritated that it isn't public so I don't much care.
+ //
+ [DllImport("libgdk_pixbuf-2.0-0.dll")]
+ static extern bool gdk_pixbuf_set_option(IntPtr raw, string key, string value);
+
+ public static bool SetOption(Gdk.Pixbuf pixbuf, string key, string value)
+ {
+
+ if (value != null)
+ return gdk_pixbuf_set_option(pixbuf.Handle, key, value);
+ else
+ return false;
+ }
+
+ public static void CopyThumbnailOptions (Gdk.Pixbuf src, Gdk.Pixbuf dest)
+ {
+ if (src != null && dest != null) {
+ PixbufUtils.SetOption (dest, "tEXt::Thumb::URI", src.GetOption ("tEXt::Thumb::URI"));
+ PixbufUtils.SetOption (dest, "tEXt::Thumb::MTime", src.GetOption ("tEXt::Thumb::MTime"));
+ }
+ }
+
+ public static void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream, string type, string [] options, string [] values)
+ {
+ byte [] data;
+
+ data = PixbufUtils.Save (pixbuf, type, options, values);
+ stream.Write (data, 0, data.Length);
+ }
+
+ static string [] NullTerminateArray (string [] options)
+ {
+ string [] terminated_options = options;
+
+ if (options != null && options [ options.Length - 1 ] != null) {
+ terminated_options = new string [options.Length + 1];
+ Array.Copy (options, terminated_options, options.Length);
+ }
+
+ return terminated_options;
+ }
+
+ [DllImport("libgdk_pixbuf-2.0-0.dll")]
+ static extern bool gdk_pixbuf_save_to_bufferv (IntPtr raw, out IntPtr data, out IntPtr length,
+ string type,
+ string [] keys, string [] values, out IntPtr error);
+
+
+ public static byte [] Save (Gdk.Pixbuf pixbuf, string type, string [] options, string [] values)
+ {
+ IntPtr error = IntPtr.Zero;
+ IntPtr data;
+ IntPtr length;
+
+ bool success = gdk_pixbuf_save_to_bufferv (pixbuf.Handle,
+ out data,
+ out length,
+ type,
+ NullTerminateArray (options),
+ NullTerminateArray (values),
+ out error);
+
+ if (error != IntPtr.Zero)
+ throw new GLib.GException (error);
+
+ if (!success)
+ throw new ApplicationException ("Unknown error while saving file");
+
+ byte [] content = new byte [(int)length];
+ Marshal.Copy (data, content, 0, (int)length);
+
+ GLib.Marshaller.Free (data);
+
+ return content;
+ }
+
+ public static Pixbuf TagIconFromPixbuf (Pixbuf source)
+ {
+ return IconFromPixbuf (source, (int) Tag.IconSize.Large);
+ }
+
+ public static Pixbuf IconFromPixbuf (Pixbuf source, int size)
+ {
+ Pixbuf tmp = null;
+ Pixbuf icon = null;
+
+ if (source.Width > source.Height)
+ source = tmp = new Pixbuf (source, (source.Width - source.Height) /2, 0, source.Height, source.Height);
+ else if (source.Width < source.Height)
+ source = tmp = new Pixbuf (source, 0, (source.Height - source.Width) /2, source.Width, source.Width);
+
+ if (source.Width == source.Height)
+ icon = source.ScaleSimple (size, size, InterpType.Bilinear);
+ else
+ throw new Exception ("Bad logic leads to bad accidents");
+
+ if (tmp != null)
+ tmp.Dispose ();
+
+ return icon;
+ }
+
+ static public Pixbuf LoadFromScreen (Gdk.Window win) {
+ Gdk.Screen screen = win.Screen;
+ Drawable d = screen.RootWindow;
+ int monitor = screen.GetMonitorAtWindow (win);
+ Gdk.Rectangle geom = screen.GetMonitorGeometry (monitor);
+
+ //
+ // We use the screen width and height because that reflects
+ // the current resolution, the RootWindow can actually be different.
+ //
+
+ Pixbuf buf = new Pixbuf (Colorspace.Rgb, false, 8, geom.Width, geom.Height);
+
+ return buf.GetFromDrawable (d,
+ d.Colormap, geom.X, geom.Y, 0, 0,
+ geom.Width, geom.Height);
+ }
+
+ static public Pixbuf LoadFromScreen () {
+ Screen screen = Display.Default.GetScreen (0);
+ Drawable d = screen.RootWindow;
+ int width = screen.Width;
+ int height = screen.Height;
+
+ //
+ // We use the screen width and height because that reflects
+ // the current resolution, the RootWindow can actually be different.
+ //
+
+ Pixbuf buf = new Pixbuf (Colorspace.Rgb, false, 8, width, height);
+
+ return buf.GetFromDrawable (d,
+ d.Colormap, 0, 0, 0, 0,
+ width, height);
+ }
+
+ static public Pixbuf LoadFromAssembly (string resource)
+ {
+ try {
+ return new Pixbuf (System.Reflection.Assembly.GetEntryAssembly (), resource);
+ } catch {
+ return null;
+ }
+ }
+
+ [DllImport ("libc")]
+ static extern int rename (string oldpath, string newpath);
+
+ public static void SaveAtomic (Gdk.Pixbuf src, string filename, string type, string [] keys, string [] values)
+ {
+ string tmpname = filename + ".tmp";
+ src.Savev (tmpname, type, NullTerminateArray (keys), NullTerminateArray (values));
+ if (rename (tmpname, filename) < 0)
+ throw new Exception ("Error renaming file");
+ }
+
+ public static Gdk.Pixbuf ScaleToAspect (Gdk.Pixbuf orig, int width, int height)
+ {
+ Gdk.Rectangle pos;
+ double scale = Fit (orig, width, height, false, out pos.Width, out pos.Height);
+ pos.X = (width - pos.Width) / 2;
+ pos.Y = (height - pos.Height) / 2;
+
+ Pixbuf scaled = new Pixbuf (Colorspace.Rgb, false, 8, width, height);
+ scaled.Fill (0x000000);
+
+ orig.Composite (scaled, pos.X, pos.Y,
+ pos.Width, pos.Height,
+ pos.X, pos.Y, scale, scale,
+ Gdk.InterpType.Bilinear,
+ 255);
+
+ return scaled;
+ }
+
+ public static string Resize (string orig_path, int size, bool copy_meta)
+ {
+ string version_path = System.IO.Path.GetTempFileName ();
+ Resize (orig_path, version_path, size, copy_meta);
+ return version_path;
+ }
+
+ public static void Resize (string orig_path, string dest_path, int size, bool copy_meta)
+ {
+ Exif.ExifData exif_data;
+ if (copy_meta)
+ exif_data = new Exif.ExifData (orig_path);
+ else
+ exif_data = new Exif.ExifData ();
+
+ Gdk.Pixbuf image = PixbufUtils.LoadAtMaxSize (orig_path, size, size);
+
+ PixbufUtils.SaveJpeg (image, dest_path, 95, exif_data);
+ image.Dispose ();
+ }
+
+
+ public static Pixbuf Flatten (Pixbuf pixbuf)
+ {
+ if (!pixbuf.HasAlpha)
+ return null;
+
+ Pixbuf flattened = new Pixbuf (Colorspace.Rgb, false, 8, pixbuf.Width, pixbuf.Height);
+ pixbuf.CompositeColor (flattened, 0, 0,
+ pixbuf.Width, pixbuf.Height,
+ 0, 0, 1, 1,
+ InterpType.Bilinear,
+ 255, 0, 0, 2000, 0xffffff, 0xffffff);
+
+ return flattened;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct FPixbufJpegMarker {
+ public int type;
+ public byte *data;
+ public int length;
+ }
+
+ [DllImport ("libfspot")]
+ static extern bool f_pixbuf_save_jpeg (IntPtr src, string path, int quality, FPixbufJpegMarker [] markers, int num_markers);
+
+ public static void SaveJpeg (Pixbuf pixbuf, string path, int quality, Exif.ExifData exif_data)
+ {
+ Pixbuf temp = null;
+ if (pixbuf.HasAlpha) {
+ temp = Flatten (pixbuf);
+ pixbuf = temp;
+ }
+
+ // The DCF spec says thumbnails should be 160x120 always
+ Pixbuf thumbnail = ScaleToAspect (pixbuf, 160, 120);
+ byte [] thumb_data = Save (thumbnail, "jpeg", null, null);
+ exif_data.Data = thumb_data;
+ thumbnail.Dispose ();
+
+ // Most of the things we will set will be in the 0th ifd
+ Exif.ExifContent content = exif_data.GetContents (Exif.Ifd.Zero);
+
+ // reset the orientation tag the default is top/left
+ content.GetEntry (Exif.Tag.Orientation).Reset ();
+
+ // set the write time in the datetime tag
+ content.GetEntry (Exif.Tag.DateTime).Reset ();
+
+ // set the software tag
+ content.GetEntry (Exif.Tag.Software).SetData (FSpot.Defines.PACKAGE + " version " + FSpot.Defines.VERSION);
+
+ byte [] data = exif_data.Save ();
+ FPixbufJpegMarker [] marker = new FPixbufJpegMarker [0];
+ bool result = false;
+
+ unsafe {
+ if (data.Length > 0) {
+
+ fixed (byte *p = data) {
+ marker = new FPixbufJpegMarker [1];
+ marker [0].type = 0xe1; // APP1 marker
+ marker [0].data = p;
+ marker [0].length = data.Length;
+
+ result = f_pixbuf_save_jpeg (pixbuf.Handle, path, quality, marker, marker.Length);
+ }
+ } else
+ result = f_pixbuf_save_jpeg (pixbuf.Handle, path, quality, marker, marker.Length);
+
+ }
+
+ if (temp != null)
+ temp.Dispose ();
+
+ if (result == false)
+ throw new System.Exception ("Error Saving File");
+ }
+
+
+ [DllImport ("libfspot")]
+ static extern IntPtr f_pixbuf_unsharp_mask (IntPtr src, double radius, double amount, double threshold);
+
+ public static Pixbuf UnsharpMask (Pixbuf src, double radius, double amount, double threshold)
+ {
+ IntPtr raw_ret = f_pixbuf_unsharp_mask (src.Handle, radius, amount, threshold);
+ Gdk.Pixbuf ret = (Gdk.Pixbuf) GLib.Object.GetObject(raw_ret, true);
+ return ret;
+ }
+
+ [DllImport ("libfspot")]
+ static extern IntPtr f_pixbuf_blur (IntPtr src, double radius);
+
+ public static Pixbuf Blur (Pixbuf src, double radius)
+ {
+ IntPtr raw_ret = f_pixbuf_blur (src.Handle, radius);
+ Gdk.Pixbuf ret = (Gdk.Pixbuf) GLib.Object.GetObject(raw_ret, true);
+ return ret;
+ }
+
+#if OLDREDEYE
+ [DllImport ("libfspot")]
+ static extern void f_pixbuf_remove_redeye (IntPtr src);
+
+ public static Gdk.Pixbuf RemoveRedeye (Gdk.Pixbuf src, Gdk.Rectangle area)
+#else
+ public unsafe static Gdk.Pixbuf RemoveRedeye (Gdk.Pixbuf src, Gdk.Rectangle area)
+ {
+ return RemoveRedeye (src, area, -15);
+ }
+
+ public unsafe static Gdk.Pixbuf RemoveRedeye (Gdk.Pixbuf src, Gdk.Rectangle area, int threshold)
+ //threshold, factors and comparisons borrowed from the gimp plugin 'redeye.c' by Robert Merkel
+#endif
+ {
+ Gdk.Pixbuf copy = src.Copy ();
+ Gdk.Pixbuf selection = new Gdk.Pixbuf (copy, area.X, area.Y, area.Width, area.Height);
+#if OLREDEYE
+ f_pixbuf_remove_redeye (selection.Handle);
+ selection.Dispose ();
+#else
+ byte *spix = (byte *)selection.Pixels;
+ int h = selection.Height;
+ int w = selection.Width;
+ int channels = src.NChannels;
+
+ double RED_FACTOR = 0.5133333;
+ double GREEN_FACTOR = 1;
+ double BLUE_FACTOR = 0.1933333;
+
+ for (int j = 0; j < h; j++) {
+ byte *s = spix;
+ for (int i = 0; i < w; i++) {
+ int adjusted_red = (int)(s[0] * RED_FACTOR);
+ int adjusted_green = (int)(s[1] * GREEN_FACTOR);
+ int adjusted_blue = (int)(s[2] * BLUE_FACTOR);
+
+ if (adjusted_red >= adjusted_green - threshold
+ && adjusted_red >= adjusted_blue - threshold)
+ s[0] = (byte)(((double)(adjusted_green + adjusted_blue)) / (2.0 * RED_FACTOR));
+ s += channels;
+ }
+ spix += selection.Rowstride;
+ }
+
+#endif
+ return copy;
+ }
+
+ public static unsafe Pixbuf ColorAdjust (Pixbuf src, double brightness, double contrast,
+ double hue, double saturation, int src_color, int dest_color)
+ {
+ Pixbuf adjusted = new Pixbuf (Colorspace.Rgb, src.HasAlpha, 8, src.Width, src.Height);
+ PixbufUtils.ColorAdjust (src, adjusted, brightness, contrast, hue, saturation, src_color, dest_color);
+ return adjusted;
+ }
+
+ public static Cms.Format PixbufCmsFormat (Pixbuf buf)
+ {
+ return buf.HasAlpha ? Cms.Format.Rgba8Planar : Cms.Format.Rgb8;
+ }
+
+ public static unsafe void ColorAdjust (Pixbuf src, Pixbuf dest,
+ double brightness, double contrast,
+ double hue, double saturation,
+ int src_color, int dest_color)
+ {
+ if (src.Width != dest.Width || src.Height != dest.Height)
+ throw new Exception ("Invalid Dimensions");
+
+ //Cms.Profile eos10d = new Cms.Profile ("/home/lewing/ICCProfiles/EOS-10D-True-Color-Non-Linear.icm");
+ Cms.Profile srgb = Cms.Profile.CreateStandardRgb ();
+
+ Cms.Profile bchsw = Cms.Profile.CreateAbstract (256,
+ 0.0,
+ brightness, contrast,
+ hue, saturation, src_color,
+ dest_color);
+
+ Cms.Profile [] list = new Cms.Profile [] { srgb, bchsw, srgb };
+ Cms.Transform trans = new Cms.Transform (list,
+ PixbufCmsFormat (src),
+ PixbufCmsFormat (dest),
+ Cms.Intent.Perceptual, 0x0100);
+
+ ColorAdjust (src, dest, trans);
+
+ trans.Dispose ();
+ srgb.Dispose ();
+ bchsw.Dispose ();
+ }
+
+
+ public static unsafe void ColorAdjust (Gdk.Pixbuf src, Gdk.Pixbuf dest, Cms.Transform trans)
+ {
+ int width = src.Width;
+ byte * srcpix = (byte *) src.Pixels;
+ byte * destpix = (byte *) dest.Pixels;
+
+ for (int row = 0; row < src.Height; row++) {
+ trans.Apply ((IntPtr) (srcpix + row * src.Rowstride),
+ (IntPtr) (destpix + row * dest.Rowstride),
+ (uint)width);
+ }
+
+ }
+
+ public static unsafe bool IsGray (Gdk.Pixbuf pixbuf, int max_difference)
+ {
+ int chan = pixbuf.NChannels;
+
+ byte *pix = (byte *)pixbuf.Pixels;
+ int h = pixbuf.Height;
+ int w = pixbuf.Width;
+ int stride = pixbuf.Rowstride;
+
+ for (int j = 0; j < h; j++) {
+ byte *p = pix;
+ for (int i = 0; i < w; i++) {
+ if (Math.Abs (p[0] - p[1]) > max_difference || Math.Abs (p[0] - p [2]) > max_difference) {
+ goto Found;
+ }
+ p += chan;
+ }
+ pix += stride;
+ }
+
+ return true;
+
+ Found:
+ return false;
+ }
+
+ public static unsafe void ReplaceColor (Gdk.Pixbuf src, Gdk.Pixbuf dest)
+ {
+ if (src.HasAlpha || !dest.HasAlpha || (src.Width != dest.Width) || (src.Height != dest.Height))
+ throw new ApplicationException ("invalid pixbufs");
+
+ byte *dpix = (byte *)dest.Pixels;
+ byte *spix = (byte *)src.Pixels;
+ int h = src.Height;
+ int w = src.Width;
+ for (int j = 0; j < h; j++) {
+ byte *d = dpix;
+ byte *s = spix;
+ for (int i = 0; i < w; i++) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d += 4;
+ s += 3;
+ }
+ dpix += dest.Rowstride;
+ spix += src.Rowstride;
+ }
+ }
+
+ public static Gdk.Pixbuf GetThumbnail (Exif.ExifData data)
+ {
+ byte [] thumb_data = data.Data;
+ if (thumb_data.Length > 0) {
+ PixbufOrientation orientation = GetOrientation (data);
+
+ using (MemoryStream mem = new MemoryStream (thumb_data)) {
+ Gdk.Pixbuf thumb = new Gdk.Pixbuf (mem);
+
+ Gdk.Pixbuf rotated = PixbufUtils.TransformOrientation (thumb, orientation);
+
+ if (rotated != thumb)
+ thumb.Dispose ();
+ return rotated;
+ }
+ }
+ return null;
+ }
+
+ public static PixbufOrientation GetOrientation (Exif.ExifData data)
+ {
+ PixbufOrientation orientation = PixbufOrientation.TopLeft;
+
+ Exif.ExifEntry e = data.GetContents (Exif.Ifd.Zero).Lookup (Exif.Tag.Orientation);
+
+ if (e != null) {
+ ushort [] value = e.GetDataUShort ();
+ orientation = (PixbufOrientation) value [0];
+ }
+
+ return orientation;
+ }
+
+ public static PixbufOrientation GetOrientation (string path)
+ {
+ using (FSpot.ImageFile img = FSpot.ImageFile.Create (path)) {
+ return img.Orientation;
+ }
+ }
+
+ [DllImport("libgnomeui-2-0.dll")]
+ static extern IntPtr gnome_thumbnail_scale_down_pixbuf(IntPtr pixbuf, int dest_width, int dest_height);
+
+ public static Gdk.Pixbuf ScaleDown (Gdk.Pixbuf src, int width, int height)
+ {
+ IntPtr raw_ret = gnome_thumbnail_scale_down_pixbuf(src.Handle, width, height);
+ Gdk.Pixbuf ret;
+ if (raw_ret == IntPtr.Zero)
+ ret = null;
+ else
+ ret = (Gdk.Pixbuf) GLib.Object.GetObject(raw_ret, true);
+ return ret;
+ }
+
+ public static Gdk.Pixbuf TransformOrientation (Gdk.Pixbuf src, PixbufOrientation orientation, bool copy_data)
+ {
+ Gdk.Pixbuf pixbuf;
+ if (src == null)
+ return null;
+
+ switch (orientation) {
+ case PixbufOrientation.LeftTop:
+ case PixbufOrientation.LeftBottom:
+ case PixbufOrientation.RightTop:
+ case PixbufOrientation.RightBottom:
+ pixbuf = new Gdk.Pixbuf (src.Colorspace, src.HasAlpha,
+ src.BitsPerSample,
+ src.Height, src.Width);
+ break;
+ case PixbufOrientation.TopRight:
+ case PixbufOrientation.BottomRight:
+ case PixbufOrientation.BottomLeft:
+ pixbuf = new Gdk.Pixbuf (src.Colorspace, src.HasAlpha,
+ src.BitsPerSample,
+ src.Width, src.Height);
+ break;
+ default:
+ pixbuf = src;
+ break;
+ }
+
+ if (copy_data && src != pixbuf)
+ TransformAndCopy (src, pixbuf, orientation, new Gdk.Rectangle (0, 0, src.Width, src.Height));
+
+ return pixbuf;
+ }
+
+ public static Gdk.Pixbuf TransformOrientation (Gdk.Pixbuf src, PixbufOrientation orientation)
+ {
+ return TransformOrientation (src, orientation, true);
+ }
+
+ public static Gdk.Rectangle TransformOrientation (Gdk.Pixbuf src, Gdk.Rectangle args, PixbufOrientation orientation)
+ {
+ return TransformOrientation (src.Width, src.Height, args, orientation);
+ }
+
+ public static Gdk.Rectangle TransformOrientation (int total_width, int total_height, Gdk.Rectangle args, PixbufOrientation orientation)
+ {
+ Gdk.Rectangle area = args;
+
+ switch (orientation) {
+ case PixbufOrientation.BottomRight:
+ area.X = total_width - args.X - args.Width;
+ area.Y = total_height - args.Y - args.Height;
+ break;
+ case PixbufOrientation.TopRight:
+ area.X = total_width - args.X - args.Width;
+ break;
+ case PixbufOrientation.BottomLeft:
+ area.Y = total_height - args.Y - args.Height;
+ break;
+ case PixbufOrientation.LeftTop:
+ area.X = args.Y;
+ area.Y = args.X;
+ area.Width = args.Height;
+ area.Height = args.Width;
+ break;
+ case PixbufOrientation.RightBottom:
+ area.X = total_height - args.Y - args.Height;
+ area.Y = total_width - args.X - args.Width;
+ area.Width = args.Height;
+ area.Height = args.Width;
+ break;
+ case PixbufOrientation.RightTop:
+ area.X = total_height - args.Y - args.Height;
+ area.Y = args.X;
+ area.Width = args.Height;
+ area.Height = args.Width;
+ break;
+ case PixbufOrientation.LeftBottom:
+ area.X = args.Y;
+ area.Y = total_width - args.X - args.Width;
+ area.Width = args.Height;
+ area.Height = args.Width;
+ break;
+ default:
+ break;
+ }
+
+ return area;
+ }
+
+ public static Gdk.Rectangle TransformAndCopy (Gdk.Pixbuf src, Gdk.Pixbuf dest, PixbufOrientation orientation, Gdk.Rectangle args)
+ {
+ Gdk.Rectangle area = TransformOrientation (src, args, orientation);
+
+ int step = 256;
+
+ Gdk.Rectangle rect = new Gdk.Rectangle (args.X, args.Y,
+ Math.Min (step, args.Width),
+ Math.Min (step, args.Height));
+
+ Gdk.Rectangle trect = TransformOrientation (src, rect, orientation);
+ Gdk.Pixbuf tmp = new Gdk.Pixbuf (src.Colorspace, src.HasAlpha,
+ src.BitsPerSample,
+ trect.Width, trect.Height);
+
+ Gdk.Rectangle subarea;
+ BlockProcessor proc = new BlockProcessor (args, 256);
+ while (proc.Step (out subarea)) {
+ Gdk.Rectangle trans = TransformOrientation (src, subarea, orientation);
+ Gdk.Pixbuf ssub = new Gdk.Pixbuf (src, subarea.X, subarea.Y,
+ subarea.Width, subarea.Height);
+
+ Gdk.Pixbuf tsub = new Gdk.Pixbuf (tmp, 0, 0, trans.Width, trans.Height);
+ CopyWithOrientation (ssub, tsub, orientation);
+
+ tsub.CopyArea (0, 0, trans.Width, trans.Height, dest, trans.X, trans.Y);
+ ssub.Dispose ();
+ tsub.Dispose ();
+ }
+
+ tmp.Dispose ();
+ return area;
+ }
+ // Bindings from libf.
+
+ [DllImport ("libfspot")]
+ static extern IntPtr f_pixbuf_copy_apply_brightness_and_contrast (IntPtr src, float brightness, float contrast);
+
+ public static Pixbuf ApplyBrightnessAndContrast (Pixbuf src, float brightness, float contrast)
+ {
+ return new Pixbuf (f_pixbuf_copy_apply_brightness_and_contrast (src.Handle, brightness, contrast));
+ }
+
+ [DllImport ("libfspot")]
+ static extern bool f_pixbuf_save_jpeg_atomic (IntPtr pixbuf, string filename, int quality, out IntPtr error);
+
+ public static void SaveAsJpegAtomically (Pixbuf pixbuf, string filename, int quality)
+ {
+ IntPtr error = IntPtr.Zero;
+
+ if (! f_pixbuf_save_jpeg_atomic (pixbuf.Handle, filename, quality, out error)) {
+ throw new GLib.GException (error);
+ }
+ }
+
+ [DllImport ("libfspot")]
+ static extern void f_pixbuf_copy_with_orientation (IntPtr src, IntPtr dest, int orientation);
+
+ public static void CopyWithOrientation (Gdk.Pixbuf src, Gdk.Pixbuf dest, PixbufOrientation orientation)
+ {
+ f_pixbuf_copy_with_orientation (src.Handle, dest.Handle, (int)orientation);
+ }
+
+#if false
+ [DllImport("glibsharpglue")]
+ static extern int gtksharp_object_get_ref_count (IntPtr obj);
+
+ public static int RefCount (GLib.Object obj) {
+ return gtksharp_object_get_ref_count (obj.Handle);
+ }
+#endif
+#endif
+}
Added: trunk/beagle/Util/F-Spot/README
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/README Sun Apr 13 20:18:35 2008
@@ -0,0 +1,2 @@
+http://svn.gnome.org/viewvc/f-spot/trunk/src/
+Imported from F-Spot svn r3823.
Added: trunk/beagle/Util/F-Spot/upstream-changes/01-initial-diff-r3823.diff
==============================================================================
--- (empty file)
+++ trunk/beagle/Util/F-Spot/upstream-changes/01-initial-diff-r3823.diff Sun Apr 13 20:18:35 2008
@@ -0,0 +1,464 @@
+Initial diff against F-Spot r3823.
+
+diff -u -r ./Imaging/Ciff.cs ../../../FSpot/Imaging/Ciff.cs
+--- ./Imaging/Ciff.cs 2008-04-13 13:52:26.000000000 -0400
++++ ../../../FSpot/Imaging/Ciff.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -454,7 +454,6 @@
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+-#if false
+ public override Gdk.Pixbuf Load (int width, int height)
+ {
+ Gdk.Pixbuf full = this.Load ();
+@@ -462,7 +461,7 @@
+ full.Dispose ();
+ return scaled;
+ }
+-#endif
++
+ public void Dump ()
+ {
+ Root.Dump ();
+diff -u -r ./Imaging/DCRawFile.cs ../../../FSpot/Imaging/DCRawFile.cs
+--- ./Imaging/DCRawFile.cs 2008-04-13 14:17:22.000000000 -0400
++++ ../../../FSpot/Imaging/DCRawFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -89,18 +89,14 @@
+
+ internal static System.IO.Stream RawPixbufStream (Uri location)
+ {
+-#if false
+ string path = location.LocalPath;
+ string [] args = new string [] { dcraw_command, "-h", "-w", "-c", "-t", "0", path };
+
+ InternalProcess proc = new InternalProcess (System.IO.Path.GetDirectoryName (path), args);
+ proc.StandardInput.Close ();
+ return proc.StandardOutput;
+-#else
+- return null;
+-#endif
+ }
+-
++
+ public static System.IO.Stream RawPixbufStreamOld (string path)
+ {
+ // FIXME this filename quoting is super lame
+@@ -114,7 +110,6 @@
+ return new Pipe (process, process.StandardOutput.BaseStream);
+ }
+
+-#if false
+ public static Gdk.Pixbuf Load (string path, string args)
+ {
+ // FIXME this filename quoting is super lame
+@@ -130,6 +125,5 @@
+ return PixbufUtils.LoadFromStream (process.StandardOutput.BaseStream);
+ }
+ }
+-#endif
+ }
+ }
+diff -u -r ./Imaging/ImageFile.cs ../../../FSpot/Imaging/ImageFile.cs
+--- ./Imaging/ImageFile.cs 2008-04-13 14:11:44.000000000 -0400
++++ ../../../FSpot/Imaging/ImageFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -1,6 +1,6 @@
+ using System;
+ using System.IO;
+-using UriUtils = Beagle.Util.UriFu;
++using FSpot.Utils;
+
+ namespace FSpot {
+ public class ImageFormatException : ApplicationException {
+@@ -30,12 +30,10 @@
+ // FIXME, this seems like the sane thing to do, but vfs streams seem to
+ // actually be faster and they need more testing.
+ //if (vfs.IsLocal)
+- return File.OpenRead (uri.LocalPath);
++ // return File.OpenRead (uri.LocalPath);
+
+-#if false
+ System.Console.WriteLine ("open uri = {0}", uri.ToString ());
+ return new Gnome.Vfs.VfsStream (uri.ToString (), FileMode.Open);
+-#endif
+ }
+
+ public virtual Stream PixbufStream ()
+@@ -83,7 +81,6 @@
+ get { return null; }
+ }
+
+-#if false
+ public virtual void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+ throw new NotImplementedException ();
+@@ -125,23 +122,21 @@
+ return aspect.Load (stream, Orientation);
+ }
+ }
+-#endif
++
+ public virtual PixbufOrientation GetOrientation ()
+ {
+ return PixbufOrientation.TopLeft;
+ }
+-#if false
++
+ // FIXME this need to have an intent just like the loading stuff.
+ public virtual Cms.Profile GetProfile ()
+ {
+ return null;
+ }
+-#endif
++
+ public virtual System.DateTime Date
+ {
+ get {
+- return System.IO.File.GetCreationTimeUtc (uri.LocalPath);
+-#if false
+ // FIXME mono uses the file change time (ctime) incorrectly
+ // as the creation time so we try to work around that slightly
+ Gnome.Vfs.FileInfo info = new Gnome.Vfs.FileInfo (uri.ToString ());
+@@ -153,7 +148,6 @@
+ return create;
+ else
+ return write;
+-#endif
+ }
+ }
+
+diff -u -r ./Imaging/JpegFile.cs ../../../FSpot/Imaging/JpegFile.cs
+--- ./Imaging/JpegFile.cs 2008-04-13 14:20:17.000000000 -0400
++++ ../../../FSpot/Imaging/JpegFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -10,9 +10,7 @@
+
+ namespace FSpot {
+ public interface IThumbnailContainer {
+-#if false
+ Gdk.Pixbuf GetEmbeddedThumbnail ();
+-#endif
+ }
+
+ public class JpegFile : ImageFile, IThumbnailContainer, SemWeb.StatementSource {
+@@ -64,12 +62,11 @@
+ Header.Select (sink);
+ }
+
+-#if false
+ public override Cms.Profile GetProfile ()
+ {
+ return Header.GetProfile ();
+ }
+-#endif
++
+ public override string Description {
+ get {
+ #if USE_TIFF
+@@ -120,7 +117,7 @@
+ private void UpdateMeta ()
+ {
+ Exif.ExifContent image_content = this.ExifData.GetContents (Exif.Ifd.Zero);
+- image_content.GetEntry (Exif.Tag.Software).SetData ("F-Spot" + " version " + "r3823");
++ image_content.GetEntry (Exif.Tag.Software).SetData (FSpot.Defines.PACKAGE + " version " + FSpot.Defines.VERSION);
+
+ // set the write time in the datetime tag
+ image_content.GetEntry (Exif.Tag.DateTime).Reset ();
+@@ -141,7 +138,6 @@
+ // Console.WriteLine ("saved");
+ }
+
+-#if false
+ public void SaveMetaData (string path)
+ {
+ UpdateMeta ();
+@@ -187,7 +183,6 @@
+ // now update the exif data
+ ExifData.Data = thumb_data;
+ }
+-#endif
+
+ public void SetDimensions (int width, int height)
+ {
+@@ -213,7 +208,6 @@
+ image_content.GetEntry (Exif.Tag.PixelYDimension).SetData ((uint)height);
+ }
+
+-#if false
+ public override void Save (Gdk.Pixbuf pixbuf, System.IO.Stream stream)
+ {
+
+@@ -252,7 +246,7 @@
+ }
+ return null;
+ }
+-#endif
++
+ public Exif.ExifData ExifData {
+ get {
+ if (exif_data == null) {
+diff -u -r ./Imaging/JpegHeader.cs ../../../FSpot/Imaging/JpegHeader.cs
+--- ./Imaging/JpegHeader.cs 2008-04-13 13:55:40.000000000 -0400
++++ ../../../FSpot/Imaging/JpegHeader.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -294,7 +294,6 @@
+ return FindMarker (new Signature (id, name));
+ }
+
+-#if false
+ public Cms.Profile GetProfile ()
+ {
+ Marker m = FindMarker (IccProfileSignature);
+@@ -312,7 +311,7 @@
+
+ return null;
+ }
+-#endif
++
+ public FSpot.Tiff.Header GetExifHeader ()
+ {
+ string name = ExifSignature.Name;
+diff -u -r ./Imaging/MrwFile.cs ../../../FSpot/Imaging/MrwFile.cs
+--- ./Imaging/MrwFile.cs 2008-04-13 13:53:37.000000000 -0400
++++ ../../../FSpot/Imaging/MrwFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -217,7 +217,6 @@
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+-#if false
+ public override Gdk.Pixbuf Load ()
+ {
+ using (System.IO.Stream stream = Open ()) {
+@@ -229,7 +228,7 @@
+ {
+ return PixbufUtils.ScaleToMaxSize (this.Load (), width, height);
+ }
+-#endif
++
+ protected void LoadBlocks ()
+ {
+ using (System.IO.Stream file = Open ()) {
+diff -u -r ./Imaging/PngFile.cs ../../../FSpot/Imaging/PngFile.cs
+--- ./Imaging/PngFile.cs 2008-04-13 14:21:09.000000000 -0400
++++ ../../../FSpot/Imaging/PngFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -1,5 +1,6 @@
+ using ICSharpCode.SharpZipLib.Zip.Compression;
+ using SemWeb;
++using Cms;
+ using System.IO;
+ using FSpot.Xmp;
+ using System.Collections;
+@@ -427,7 +428,7 @@
+ this.Time = System.DateTime.Now;
+ }
+ }
+-#if false
++
+ public class StandardRgbChunk : Chunk {
+ public StandardRgbChunk (string name, byte [] data) : base (name, data) {}
+
+@@ -437,7 +438,7 @@
+ }
+ }
+ }
+-#endif
++
+ public class GammaChunk : Chunk {
+ public GammaChunk (string name, byte [] data) : base (name, data) {}
+ private const int divisor = 100000;
+@@ -687,9 +688,7 @@
+ name_table ["cHRM"] = typeof (ColorChunk);
+ name_table ["pHYs"] = typeof (PhysChunk);
+ name_table ["gAMA"] = typeof (GammaChunk);
+-#if false
+ name_table ["sRGB"] = typeof (StandardRgbChunk);
+-#endif
+ }
+
+ protected Chunk ()
+@@ -851,7 +850,6 @@
+ }
+ }
+
+-#if false
+ public class ScanlineDecoder {
+ int width;
+ int height;
+@@ -1168,7 +1166,7 @@
+ }
+ return pixbuf;
+ }
+-#endif
++
+ private static byte [] magic = new byte [] { 137, 80, 78, 71, 13, 10, 26, 10 };
+
+
+@@ -1273,7 +1271,6 @@
+ Header.Save (stream);
+ }
+
+-#if false
+ public void Save (string path)
+ {
+ string temp_path = path + ".tmp.png";
+@@ -1360,7 +1357,7 @@
+
+ return null;
+ }
+-#endif
++
+ public override string Description {
+ get {
+ string description = Header.LookupText ("Description");
+diff -u -r ./Imaging/PnmFile.cs ../../../FSpot/Imaging/PnmFile.cs
+--- ./Imaging/PnmFile.cs 2008-04-13 13:34:17.000000000 -0400
++++ ../../../FSpot/Imaging/PnmFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -1,3 +1,4 @@
++using FSpot.Imaging;
+ using SemWeb;
+ using System;
+ using System.IO;
+@@ -116,7 +117,6 @@
+ return data;
+ }
+
+-#if false
+ static Gdk.Pixbuf LoadRGB16 (Stream stream, int width, int height)
+ {
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, 8, width, height);
+@@ -280,7 +280,6 @@
+ throw new System.Exception (System.String.Format ("unknown pnm type {0}", header.Magic));
+ }
+ }
+-#endif
+ }
+
+ #if ENABLE_NUNIT
+diff -u -r ./Imaging/RafFile.cs ../../../FSpot/Imaging/RafFile.cs
+--- ./Imaging/RafFile.cs 2008-04-13 13:53:16.000000000 -0400
++++ ../../../FSpot/Imaging/RafFile.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -69,7 +69,6 @@
+ return DCRawFile.RawPixbufStream (uri);
+ }
+
+-#if false
+ public override Gdk.Pixbuf Load ()
+ {
+ return new Gdk.Pixbuf (PixbufStream ());
+@@ -83,7 +82,7 @@
+ full.Dispose ();
+ return scaled;
+ }
+-#endif
++
+ public void Select (SemWeb.StatementSink sink)
+ {
+ byte [] data = GetEmbeddedJpeg ();
+diff -u -r ./Imaging/Tiff.cs ../../../FSpot/Imaging/Tiff.cs
+--- ./Imaging/Tiff.cs 2008-04-13 13:55:18.000000000 -0400
++++ ../../../FSpot/Imaging/Tiff.cs 2008-04-13 13:25:54.000000000 -0400
+@@ -1318,7 +1318,6 @@
+ return new DirectoryEntry (input, start, header_endian);
+ }
+
+-#if false
+ public Cms.Profile GetProfile ()
+ {
+ Cms.ColorCIExyY whitepoint = new Cms.ColorCIExyY (0, 0, 0);
+@@ -1405,7 +1404,6 @@
+
+ return new Cms.Profile (whitepoint, primaries, transfer);
+ }
+-#endif
+
+ public void Dump (string name)
+ {
+@@ -1583,7 +1581,6 @@
+ }
+
+
+-#if false
+ public Gdk.Pixbuf LoadPixbuf (System.IO.Stream stream)
+ {
+ Gdk.Pixbuf dest = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, width, height);
+@@ -1598,7 +1595,6 @@
+ }
+ }
+ }
+-#endif
+ }
+ #endif
+
+@@ -2108,7 +2104,6 @@
+ return file;
+ }
+
+-#if false
+ public Gdk.Pixbuf LoadJpegInterchangeFormat (ImageDirectory directory)
+ {
+ uint offset = directory.Lookup (TagId.JPEGInterchangeFormat).ValueAsLong [0];
+@@ -2135,7 +2130,6 @@
+ return result;
+ }
+ }
+-#endif
+ }
+
+ public class DngFile : TiffFile {
+@@ -2266,7 +2260,6 @@
+ } while (i < sub.Directory.Length);
+ }
+
+-#if false
+ public Gdk.Pixbuf GetEmbeddedThumbnail ()
+ {
+ using (System.IO.Stream stream = Open ()) {
+@@ -2284,7 +2277,6 @@
+ return DCRawFile.RawPixbufStream (uri);
+ }
+ }
+-#endif
+ }
+
+
+@@ -2304,14 +2296,13 @@
+ }
+ */
+
+-#if false
+ public Gdk.Pixbuf GetEmbeddedThumbnail ()
+ {
+ ImageDirectory directory;
+ directory = Header.Directory.NextDirectory;
+ return TransformAndDispose (LoadJpegInterchangeFormat (directory));
+ }
+-#endif
++
+
+ public override System.IO.Stream PixbufStream ()
+ {
+diff -u -r ./PixbufUtils.cs ../../../FSpot/PixbufUtils.cs
+--- ./PixbufUtils.cs 2008-04-13 13:50:20.000000000 -0400
++++ ../../../FSpot/PixbufUtils.cs 2008-04-13 13:45:27.000000000 -0400
+@@ -1,8 +1,10 @@
++using Gdk;
+ using System.Collections;
+ using System.Runtime.InteropServices;
+ using System;
+ using System.IO;
+ using FSpot;
++using FSpot.Utils;
+
+ /**
+ 1 2 3 4 5 6 7 8
+@@ -55,7 +57,6 @@
+ return orientation;
+ }
+
+-#if false
+ static Pixbuf error_pixbuf = null;
+ public static Pixbuf ErrorPixbuf {
+ get {
+@@ -940,5 +941,4 @@
+ return gtksharp_object_get_ref_count (obj.Handle);
+ }
+ #endif
+-#endif
+ }
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]