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); } public override Gdk.Pixbuf Load () { // FIXME this is a hack. No, really, I mean it. byte [] data = GetEmbeddedJpeg (); if (data != null) { Gdk.PixbufLoader loader = new Gdk.PixbufLoader (); loader.Write (data, (ulong)data.Length); Gdk.Pixbuf pixbuf = loader.Pixbuf; loader.Close (); return pixbuf; } return null; } 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; } 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; } } } }