You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pu...@apache.org on 2012/06/21 00:52:48 UTC

[5/5] wp7 commit: Capture parses EXIF data and rotates image based on EXIF_Orientation

Capture parses EXIF data and rotates image based on EXIF_Orientation


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/commit/f2703aa4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/tree/f2703aa4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/diff/f2703aa4

Branch: refs/heads/master
Commit: f2703aa49625867dd0f14c425d77666f8c7f779e
Parents: c42bbd5
Author: Jesse MacFadyen <pu...@gmail.com>
Authored: Wed Jun 20 15:20:41 2012 -0700
Committer: Jesse MacFadyen <pu...@gmail.com>
Committed: Wed Jun 20 15:20:41 2012 -0700

----------------------------------------------------------------------
 framework/Cordova/Commands/Capture.cs         |   33 +++-
 framework/Cordova/Commands/ImageExifHelper.cs |  194 ++++++++++++++++++++
 framework/WP7CordovaClassLib.csproj           |   10 +-
 3 files changed, 231 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/f2703aa4/framework/Cordova/Commands/Capture.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/Commands/Capture.cs b/framework/Cordova/Commands/Capture.cs
index 8207c68..25d22e3 100644
--- a/framework/Cordova/Commands/Capture.cs
+++ b/framework/Cordova/Commands/Capture.cs
@@ -25,6 +25,8 @@ using WP7CordovaClassLib.Cordova.UI;
 using AudioResult = WP7CordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
 using VideoResult = WP7CordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
 using System.Windows;
+using System.Diagnostics;
+using Microsoft.Phone.Controls;
 
 namespace WP7CordovaClassLib.Cordova.Commands
 {
@@ -433,6 +435,7 @@ namespace WP7CordovaClassLib.Cordova.Commands
             }
         }
 
+
         /// <summary>
         /// Handles result of capture to save image information 
         /// </summary>
@@ -458,14 +461,34 @@ namespace WP7CordovaClassLib.Cordova.Commands
                         MediaLibrary library = new MediaLibrary();
                         Picture image = library.SavePicture(fileName, e.ChosenPhoto);
 
+                        int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+                        int newAngle = 0;
+                        switch (orient)
+                        {
+                            case ImageExifOrientation.LandscapeLeft :
+                                newAngle = 90;
+                                break;
+                            case ImageExifOrientation.PortraitUpsideDown :
+                                newAngle = 180;
+                                break;
+                            case ImageExifOrientation.LandscapeRight :
+                                newAngle = 270;
+                                break;
+                            case ImageExifOrientation.Portrait : default : break; // 0 default already set
+                        }
+
+                        Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
                         // Save image in isolated storage    
 
                         // we should return stream position back after saving stream to media library
-                        e.ChosenPhoto.Seek(0, SeekOrigin.Begin);
-                        byte[] imageBytes = new byte[e.ChosenPhoto.Length];
-                        e.ChosenPhoto.Read(imageBytes, 0, imageBytes.Length);
+                        rotImageStream.Seek(0, SeekOrigin.Begin);
+                        
+                        byte[] imageBytes = new byte[rotImageStream.Length];
+                        rotImageStream.Read(imageBytes, 0, imageBytes.Length);
+                        rotImageStream.Dispose();
                         string pathLocalStorage = this.SaveImageToLocalStorage(fileName, isoFolder, imageBytes);
-
+                        imageBytes = null;
                         // Get image data
                         MediaFile data = new MediaFile(pathLocalStorage, image);
 
@@ -695,7 +718,7 @@ namespace WP7CordovaClassLib.Cordova.Commands
                 }
                 string filePath = System.IO.Path.Combine("/" + imageFolder + "/", imageFileName);
 
-                using (var stream = isoFile.CreateFile(filePath))
+                using (IsolatedStorageFileStream stream = isoFile.CreateFile(filePath))
                 {
                     stream.Write(imageBytes, 0, imageBytes.Length);
                 }

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/f2703aa4/framework/Cordova/Commands/ImageExifHelper.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/Commands/ImageExifHelper.cs b/framework/Cordova/Commands/ImageExifHelper.cs
new file mode 100644
index 0000000..2d7aab8
--- /dev/null
+++ b/framework/Cordova/Commands/ImageExifHelper.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.IO;
+using System.Windows.Media.Imaging;
+using System.Diagnostics;
+
+namespace WP7CordovaClassLib.Cordova.Commands
+{
+    public class ImageExifOrientation
+    {
+        public const int Portrait = 1;
+        public const int PortraitUpsideDown = 3;
+        public const int LandscapeLeft = 6;
+        public const int LandscapeRight = 8;
+    }
+
+    public class ImageExifHelper
+    {
+
+        public static Stream RotateStream(Stream stream, int angle)
+        {
+            stream.Position = 0;
+            if (angle % 90 != 0 || angle < 0)
+            {
+                throw new ArgumentException();
+            }
+            if (angle % 360 == 0)
+            {
+                return stream;
+            }
+
+            angle = angle % 360;
+
+            BitmapImage bitmap = new BitmapImage();
+            bitmap.SetSource(stream);
+            WriteableBitmap wbSource = new WriteableBitmap(bitmap);
+
+            WriteableBitmap wbTarget = null;
+
+            int srcPixelWidth = wbSource.PixelWidth;
+            int srcPixelHeight = wbSource.PixelHeight;
+
+            if (angle % 180 == 0)
+            {
+                wbTarget = new WriteableBitmap(srcPixelWidth, srcPixelHeight);
+            }
+            else
+            {
+                wbTarget = new WriteableBitmap(srcPixelHeight, srcPixelWidth);
+            }
+
+            int destPixelWidth = wbTarget.PixelWidth;
+            int[] srcPxls = wbSource.Pixels;
+            int[] destPxls = wbTarget.Pixels;
+
+            // this ugly if/else is to avoid a conditional check for every pixel
+            if (angle == 90) {
+                for (int x = 0; x < srcPixelWidth; x++) {
+                    for (int y = 0; y < srcPixelHeight; y++) {
+                        destPxls[(srcPixelHeight - y - 1) + (x * destPixelWidth)] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 180) {
+                for (int x = 0; x < srcPixelWidth; x++) {
+                    for (int y = 0; y < srcPixelHeight; y++) {
+                        destPxls[(srcPixelWidth - x - 1) + (srcPixelHeight - y - 1) * srcPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+            else if (angle == 270) {
+                for (int x = 0; x < srcPixelWidth; x++) {
+                    for (int y = 0; y < srcPixelHeight; y++) {
+                        destPxls[y + (srcPixelWidth - x - 1) * destPixelWidth] = srcPxls[x + y * srcPixelWidth];
+                    }
+                }
+            }
+
+            MemoryStream targetStream = new MemoryStream();
+            wbTarget.SaveJpeg(targetStream, destPixelWidth, wbTarget.PixelHeight, 0, 100);
+            return targetStream;
+        }
+
+        public static int getImageOrientationFromStream(Stream imgStream)
+        {
+
+            // 0xFFD8 : jpgHeader
+            // 0xFFE1 :
+            // 0x???? : length of exif data
+            // 0x????, 0x???? : Chars 'E','x','i','f'
+            // 0x0000 : 2 empty bytes
+            // <== mark beginning of tags SIZE:ID:VALUE
+            // 0x???? : 'II' or 'MM' for Intel or Motorola ( always getting II on my WP7 devices ), determins littleEndian-ness
+            // 0x002A : marker value
+            // 0x???? : offset to the Image File Data
+
+            // XXXX possible space before actual tag data ... we skip to mark + offset
+
+            // 0x???? number of exif tags present
+
+            // make sure we are at the begining
+            imgStream.Seek(0, SeekOrigin.Begin);
+            BinaryReader reader = new BinaryReader(imgStream);
+
+            byte[] jpgHdr = reader.ReadBytes(2); // always (0xFFD8)
+
+            byte start = reader.ReadByte(); // 0xFF
+            byte index = reader.ReadByte(); // 0xE1
+
+            while (start == 0xFF && index != 0xE1) // This never seems to happen, todo: optimize
+            {
+                // Get the data length
+                ushort dLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                // skip along
+                reader.BaseStream.Seek(dLen - 2, SeekOrigin.Current);
+                start = reader.ReadByte();
+                index = reader.ReadByte();
+            }
+
+            // It's only success if we found the 0xFFE1 marker
+            if (start != 0xFF || index != 0xE1)
+            {
+                //   throw new Exception("Could not find Exif data block");
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 byte length of EXIF data
+            ushort exifLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            String exif = ""; // build the string
+            for (var n = 0; n < 4; n++)
+            {
+                exif += reader.ReadChar();
+            }
+            if (exif != "Exif")
+            {
+                // did not find exif data ...
+                Debug.WriteLine("Did not find EXIF data");
+                return 0;
+            }
+
+            // read 2 empty bytes
+            //ushort emptyBytes = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            reader.ReadBytes(2);
+
+            long headerMark = reader.BaseStream.Position; // where are we now <==
+
+            //bool isLEndian = (reader.ReadChar() + "" + reader.ReadChar()) == "II";
+            reader.ReadBytes(2); // 'II' or 'MM', but we don't care
+
+            if (0x002A != BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+            {
+                Debug.WriteLine("Error in data != 0x002A");
+                return 0;
+            }
+
+            // Get the offset to the IFD (image file directory)
+            ushort imgOffset = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+
+            imgStream.Position = headerMark + imgOffset;
+            ushort tagCount = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+            for (ushort x = 0; x < tagCount; x++)
+            {
+                // Orientation = 0x112, aka 274
+                if (0x112 == BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+                {
+                    ushort dType = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+                    // don't care ..
+                    uint comps = reader.ReadUInt32();
+                    byte[] tagData = reader.ReadBytes(4);
+                    int orientation = (int)tagData[0];
+                    Debug.WriteLine("orientation = " + orientation.ToString());
+                    return orientation;
+                    // 6 means rotate clockwise 90 deg
+                    // 8 means rotate counter-clockwise 90 deg
+                    // 1 means all is good
+                    // 3 means flip vertical
+                }
+                // skip to the next item, 12 bytes each
+                reader.BaseStream.Seek(10, SeekOrigin.Current);
+            }
+            return 0;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/f2703aa4/framework/WP7CordovaClassLib.csproj
----------------------------------------------------------------------
diff --git a/framework/WP7CordovaClassLib.csproj b/framework/WP7CordovaClassLib.csproj
index a84272e..b02eee4 100644
--- a/framework/WP7CordovaClassLib.csproj
+++ b/framework/WP7CordovaClassLib.csproj
@@ -96,6 +96,7 @@
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="Cordova\Commands\GeoLocation.cs" />
+    <Compile Include="Cordova\Commands\ImageExifHelper.cs" />
     <Compile Include="Cordova\Commands\Media.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -121,6 +122,9 @@
     <Compile Include="Cordova\UI\AudioRecorder.xaml.cs">
       <DependentUpon>AudioRecorder.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Cordova\UI\ImageCapture.xaml.cs">
+      <DependentUpon>ImageCapture.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Cordova\UI\NotificationBox.xaml.cs">
       <DependentUpon>NotificationBox.xaml</DependentUpon>
     </Compile>
@@ -142,6 +146,10 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Cordova\UI\ImageCapture.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Cordova\UI\NotificationBox.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
@@ -196,4 +204,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>
\ No newline at end of file