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