summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTenghui Zhu <ztenghui@google.com>2013-08-28 16:47:42 +0000
committerTenghui Zhu <ztenghui@google.com>2013-08-28 16:47:42 +0000
commit0acda98704a01e20de5af8026875789af1cd56f9 (patch)
tree5342356f3344407349f952f9658b4f8d319da66e /src
parent568ad02f9fd3993abf4b973bdd49a34737267c77 (diff)
downloadandroid_packages_apps_Snap-0acda98704a01e20de5af8026875789af1cd56f9.tar.gz
android_packages_apps_Snap-0acda98704a01e20de5af8026875789af1cd56f9.tar.bz2
android_packages_apps_Snap-0acda98704a01e20de5af8026875789af1cd56f9.zip
Revert "Add rotate support in Film Strip"
This reverts commit 568ad02f9fd3993abf4b973bdd49a34737267c77. Change-Id: I300376126b55191e4112b8aabf3f9f61cc27fef6
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/CameraActivity.java4
-rw-r--r--src/com/android/camera/Exif.java2
-rw-r--r--src/com/android/camera/MediaSaveService.java2
-rw-r--r--src/com/android/camera/PhotoModule.java6
-rw-r--r--src/com/android/camera/Storage.java5
-rw-r--r--src/com/android/camera/VideoModule.java2
-rw-r--r--src/com/android/camera/data/CameraDataAdapter.java5
-rw-r--r--src/com/android/camera/data/LocalData.java30
-rw-r--r--src/com/android/camera/data/LocalMediaData.java332
-rw-r--r--src/com/android/camera/data/MediaDetails.java4
-rw-r--r--src/com/android/camera/data/RotationTask.java161
-rw-r--r--src/com/android/camera/data/SimpleViewData.java35
-rw-r--r--src/com/android/camera/exif/ByteBufferInputStream.java48
-rw-r--r--src/com/android/camera/exif/CountedDataInputStream.java136
-rw-r--r--src/com/android/camera/exif/ExifData.java348
-rw-r--r--src/com/android/camera/exif/ExifInterface.java2407
-rw-r--r--src/com/android/camera/exif/ExifInvalidFormatException.java23
-rw-r--r--src/com/android/camera/exif/ExifModifier.java196
-rw-r--r--src/com/android/camera/exif/ExifOutputStream.java518
-rw-r--r--src/com/android/camera/exif/ExifParser.java916
-rw-r--r--src/com/android/camera/exif/ExifReader.java92
-rw-r--r--src/com/android/camera/exif/ExifTag.java1008
-rw-r--r--src/com/android/camera/exif/IfdData.java152
-rw-r--r--src/com/android/camera/exif/IfdId.java31
-rw-r--r--src/com/android/camera/exif/JpegHeader.java39
-rw-r--r--src/com/android/camera/exif/OrderedDataOutputStream.java56
-rw-r--r--src/com/android/camera/exif/Rational.java88
-rw-r--r--src/com/android/camera/ui/FilmStripView.java4
28 files changed, 172 insertions, 6478 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index d2871f687..411b29ea7 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -456,10 +456,10 @@ public class CameraActivity extends Activity
return true;
}
case R.id.action_rotate_ccw:
- localData.rotate90Degrees(this, mDataAdapter, currentDataId, false);
+ // TODO: add the functionality.
return true;
case R.id.action_rotate_cw:
- localData.rotate90Degrees(this, mDataAdapter, currentDataId, true);
+ // TODO: add the functionality.
return true;
case R.id.action_crop:
// TODO: add the functionality.
diff --git a/src/com/android/camera/Exif.java b/src/com/android/camera/Exif.java
index 91aca4505..c6ec6af50 100644
--- a/src/com/android/camera/Exif.java
+++ b/src/com/android/camera/Exif.java
@@ -18,7 +18,7 @@ package com.android.camera;
import android.util.Log;
-import com.android.camera.exif.ExifInterface;
+import com.android.gallery3d.exif.ExifInterface;
import java.io.IOException;
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
index 988f17f94..cec8b4329 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -29,7 +29,7 @@ import android.os.IBinder;
import android.provider.MediaStore.Video;
import android.util.Log;
-import com.android.camera.exif.ExifInterface;
+import com.android.gallery3d.exif.ExifInterface;
import java.io.File;
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index f56bd68f0..bb486cb62 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -56,9 +56,6 @@ import com.android.camera.CameraManager.CameraAFMoveCallback;
import com.android.camera.CameraManager.CameraPictureCallback;
import com.android.camera.CameraManager.CameraProxy;
import com.android.camera.CameraManager.CameraShutterCallback;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.exif.ExifTag;
-import com.android.camera.exif.Rational;
import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
import com.android.camera.ui.PopupManager;
import com.android.camera.ui.RotateTextToast;
@@ -66,6 +63,9 @@ import com.android.camera.util.ApiHelper;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.UsageStatistics;
import com.android.camera2.R;
+import com.android.gallery3d.exif.ExifInterface;
+import com.android.gallery3d.exif.ExifTag;
+import com.android.gallery3d.exif.Rational;
import java.io.File;
import java.io.FileNotFoundException;
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index 8aa3eca52..aa2a9721b 100644
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -32,9 +32,8 @@ import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.MediaColumns;
import android.util.Log;
-import com.android.camera.data.LocalData;
-import com.android.camera.exif.ExifInterface;
import com.android.camera.util.ApiHelper;
+import com.android.gallery3d.exif.ExifInterface;
public class Storage {
private static final String TAG = "CameraStorage";
@@ -105,7 +104,7 @@ public class Storage {
values.put(ImageColumns.TITLE, title);
values.put(ImageColumns.DISPLAY_NAME, title + ".jpg");
values.put(ImageColumns.DATE_TAKEN, date);
- values.put(ImageColumns.MIME_TYPE, LocalData.MIME_TYPE_JPEG);
+ values.put(ImageColumns.MIME_TYPE, "image/jpeg");
// Clockwise rotation in degrees. 0, 90, 180, or 270.
values.put(ImageColumns.ORIENTATION, orientation);
values.put(ImageColumns.DATA, path);
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 53cb43ae0..bd2658e1b 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -56,7 +56,6 @@ import android.widget.Toast;
import com.android.camera.CameraManager.CameraPictureCallback;
import com.android.camera.CameraManager.CameraProxy;
import com.android.camera.app.OrientationManager;
-import com.android.camera.exif.ExifInterface;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.AccessibilityUtils;
import com.android.camera.ui.PopupManager;
@@ -64,6 +63,7 @@ import com.android.camera.ui.RotateTextToast;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.UsageStatistics;
import com.android.camera2.R;
+import com.android.gallery3d.exif.ExifInterface;
import java.io.File;
import java.io.IOException;
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
index 2fbe182c9..2b9e77754 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -219,9 +219,8 @@ public class CameraDataAdapter implements LocalDataAdapter {
}
LocalData data = mImages.get(pos);
- LocalData refreshedData = data.refresh(resolver);
- if (refreshedData != null) {
- updateData(pos, refreshedData);
+ if (data.refresh(resolver)) {
+ updateData(pos, data);
}
}
diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java
index d9e5bc188..82567120e 100644
--- a/src/com/android/camera/data/LocalData.java
+++ b/src/com/android/camera/data/LocalData.java
@@ -29,15 +29,10 @@ import java.util.Comparator;
/**
* An abstract interface that represents the local media data. Also implements
* Comparable interface so we can sort in DataAdapter.
- * Note that all the sub-class of LocalData are designed to be immutable, i.e:
- * all the members need to be final, and there is no setter. In this way, we
- * can guarantee thread safety for LocalData.
*/
public interface LocalData extends FilmStripView.ImageData {
static final String TAG = "CAM_LocalData";
- public static final String MIME_TYPE_JPEG = "image/jpeg";
-
public static final int ACTION_NONE = 0;
public static final int ACTION_PLAY = 1;
public static final int ACTION_DELETE = (1 << 1);
@@ -88,17 +83,6 @@ public interface LocalData extends FilmStripView.ImageData {
/** Removes the data from the storage if possible. */
boolean delete(Context c);
- /**
- * Rotate the image in 90 degrees. This is a no-op for non-image.
- *
- * @param context Used to update the content provider when rotation is done.
- * @param adapter Used to update the view.
- * @param currentDataId Used to update the view.
- * @param clockwise True if the rotation goes clockwise.
- */
- void rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise);
-
void onFullScreen(boolean fullScreen);
/** Returns {@code true} if it allows swipe to filmstrip in full screen. */
@@ -139,17 +123,12 @@ public interface LocalData extends FilmStripView.ImageData {
int getLocalDataType();
/**
- * @return The size of the data in bytes
- */
- long getSizeInBytes();
-
- /**
* Refresh the data content.
*
* @param resolver {@link ContentResolver} to refresh the data.
- * @return A new LocalData object if success, null otherwise.
+ * @return {@code true} if success, {@code false} otherwise.
*/
- LocalData refresh(ContentResolver resolver);
+ boolean refresh(ContentResolver resolver);
static class NewestFirstComparator implements Comparator<LocalData> {
@@ -177,10 +156,5 @@ public interface LocalData extends FilmStripView.ImageData {
return cmp;
}
}
-
- /**
- * @return the Id of the data.
- */
- long getId();
}
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index 8dd0d1184..1f87b5040 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -52,18 +52,18 @@ import java.util.Locale;
* return a bitmap.
*/
public abstract class LocalMediaData implements LocalData {
- protected final long mId;
- protected final String mTitle;
- protected final String mMimeType;
- protected final long mDateTakenInSeconds;
- protected final long mDateModifiedInSeconds;
- protected final String mPath;
+ protected long id;
+ protected String title;
+ protected String mimeType;
+ protected long dateTakenInSeconds;
+ protected long dateModifiedInSeconds;
+ protected String path;
// width and height should be adjusted according to orientation.
- protected final int mWidth;
- protected final int mHeight;
- protected final long mSizeInBytes;
- protected final double mLatitude;
- protected final double mLongitude;
+ protected int width;
+ protected int height;
+ protected long sizeInBytes;
+ protected double latitude;
+ protected double longitude;
/** The panorama metadata information of this media data. */
protected PhotoSphereHelper.PanoramaMetadata mPanoramaMetadata;
@@ -77,62 +77,34 @@ public abstract class LocalMediaData implements LocalData {
*/
protected Boolean mUsing = false;
- public LocalMediaData (long id, String title, String mimeType,
- long dateTakenInSeconds, long dateModifiedInSeconds, String path,
- int width, int height, long sizeInBytes, double latitude,
- double longitude) {
- mId = id;
- mTitle = new String(title);
- mMimeType = new String(mimeType);
- mDateTakenInSeconds = dateTakenInSeconds;
- mDateModifiedInSeconds = dateModifiedInSeconds;
- mPath = new String(path);
- mWidth = width;
- mHeight = height;
- mSizeInBytes = sizeInBytes;
- mLatitude = latitude;
- mLongitude = longitude;
- return;
- }
-
@Override
public long getDateTaken() {
- return mDateTakenInSeconds;
+ return dateTakenInSeconds;
}
@Override
public long getDateModified() {
- return mDateModifiedInSeconds;
- }
-
- @Override
- public long getId() {
- return mId;
+ return dateModifiedInSeconds;
}
@Override
public String getTitle() {
- return new String(mTitle);
+ return new String(title);
}
@Override
public int getWidth() {
- return mWidth;
+ return width;
}
@Override
public int getHeight() {
- return mHeight;
+ return height;
}
@Override
public String getPath() {
- return mPath;
- }
-
- @Override
- public long getSizeInBytes() {
- return mSizeInBytes;
+ return path;
}
@Override
@@ -147,7 +119,7 @@ public abstract class LocalMediaData implements LocalData {
@Override
public boolean delete(Context ctx) {
- File f = new File(mPath);
+ File f = new File(path);
return f.delete();
}
@@ -227,11 +199,11 @@ public abstract class LocalMediaData implements LocalData {
@Override
public double[] getLatLong() {
- if (mLatitude == 0 && mLongitude == 0) {
+ if (latitude == 0 && longitude == 0) {
return null;
}
return new double[] {
- mLatitude, mLongitude
+ latitude, longitude
};
}
@@ -243,25 +215,25 @@ public abstract class LocalMediaData implements LocalData {
@Override
public String getMimeType() {
- return mMimeType;
+ return mimeType;
}
@Override
public MediaDetails getMediaDetails(Context context) {
DateFormat dateFormatter = DateFormat.getDateTimeInstance();
MediaDetails mediaDetails = new MediaDetails();
- mediaDetails.addDetail(MediaDetails.INDEX_TITLE, mTitle);
- mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, mWidth);
- mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, mHeight);
- mediaDetails.addDetail(MediaDetails.INDEX_PATH, mPath);
+ mediaDetails.addDetail(MediaDetails.INDEX_TITLE, title);
+ mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, width);
+ mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, height);
+ mediaDetails.addDetail(MediaDetails.INDEX_PATH, path);
mediaDetails.addDetail(MediaDetails.INDEX_DATETIME,
- dateFormatter.format(new Date(mDateModifiedInSeconds * 1000)));
- if (mSizeInBytes > 0) {
- mediaDetails.addDetail(MediaDetails.INDEX_SIZE, mSizeInBytes);
+ dateFormatter.format(new Date(dateModifiedInSeconds * 1000)));
+ if (sizeInBytes > 0) {
+ mediaDetails.addDetail(MediaDetails.INDEX_SIZE, sizeInBytes);
}
- if (mLatitude != 0 && mLongitude != 0) {
- String locationString = String.format(Locale.getDefault(), "%f, %f", mLatitude,
- mLongitude);
+ if (latitude != 0 && longitude != 0) {
+ String locationString = String.format(Locale.getDefault(), "%f, %f", latitude,
+ longitude);
mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, locationString);
}
return mediaDetails;
@@ -273,7 +245,7 @@ public abstract class LocalMediaData implements LocalData {
protected abstract BitmapLoadTask getBitmapLoadTask(
ImageView v, int decodeWidth, int decodeHeight);
- public static final class PhotoData extends LocalMediaData {
+ public static class PhotoData extends LocalMediaData {
private static final String TAG = "CAM_PhotoData";
public static final int COL_ID = 0;
@@ -321,69 +293,54 @@ public abstract class LocalMediaData implements LocalData {
private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024];
/** from MediaStore, can only be 0, 90, 180, 270 */
- private final int mOrientation;
-
- public PhotoData(long id, String title, String mimeType,
- long dateTakenInSeconds, long dateModifiedInSeconds,
- String path, int orientation, int width, int height,
- long sizeInBytes, double latitude, double longitude) {
- super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
- path, width, height, sizeInBytes, latitude, longitude);
- mOrientation = orientation;
- }
+ public int orientation;
static PhotoData buildFromCursor(Cursor c) {
- long id = c.getLong(COL_ID);
- String title = c.getString(COL_TITLE);
- String mimeType = c.getString(COL_MIME_TYPE);
- long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
- long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
- String path = c.getString(COL_DATA);
- int orientation = c.getInt(COL_ORIENTATION);
- int width = c.getInt(COL_WIDTH);
- int height = c.getInt(COL_HEIGHT);
- if (width <= 0 || height <= 0) {
+ PhotoData d = new PhotoData();
+ d.id = c.getLong(COL_ID);
+ d.title = c.getString(COL_TITLE);
+ d.mimeType = c.getString(COL_MIME_TYPE);
+ d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
+ d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
+ d.path = c.getString(COL_DATA);
+ d.orientation = c.getInt(COL_ORIENTATION);
+ d.width = c.getInt(COL_WIDTH);
+ d.height = c.getInt(COL_HEIGHT);
+ if (d.width <= 0 || d.height <= 0) {
Log.w(TAG, "Warning! zero dimension for "
- + path + ":" + width + "x" + height);
+ + d.path + ":" + d.width + "x" + d.height);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(path, opts);
+ BitmapFactory.decodeFile(d.path, opts);
if (opts.outWidth != -1 && opts.outHeight != -1) {
- width = opts.outWidth;
- height = opts.outHeight;
+ d.width = opts.outWidth;
+ d.height = opts.outHeight;
} else {
- Log.w(TAG, "Warning! dimension decode failed for " + path);
- Bitmap b = BitmapFactory.decodeFile(path);
+ Log.w(TAG, "Warning! dimension decode failed for " + d.path);
+ Bitmap b = BitmapFactory.decodeFile(d.path);
if (b == null) {
return null;
}
- width = b.getWidth();
- height = b.getHeight();
+ d.width = b.getWidth();
+ d.height = b.getHeight();
}
}
- if (orientation == 90 || orientation == 270) {
- int b = width;
- width = height;
- height = b;
+ if (d.orientation == 90 || d.orientation == 270) {
+ int b = d.width;
+ d.width = d.height;
+ d.height = b;
}
- long sizeInBytes = c.getLong(COL_SIZE);
- double latitude = c.getDouble(COL_LATITUDE);
- double longitude = c.getDouble(COL_LONGITUDE);
- PhotoData result = new PhotoData(id, title, mimeType, dateTakenInSeconds,
- dateModifiedInSeconds, path, orientation, width, height,
- sizeInBytes, latitude, longitude);
- return result;
- }
-
- public int getOrientation() {
- return mOrientation;
+ d.sizeInBytes = c.getLong(COL_SIZE);
+ d.latitude = c.getDouble(COL_LATITUDE);
+ d.longitude = c.getDouble(COL_LONGITUDE);
+ return d;
}
@Override
public String toString() {
- return "Photo:" + ",data=" + mPath + ",mimeType=" + mMimeType
- + "," + mWidth + "x" + mHeight + ",orientation=" + mOrientation
- + ",date=" + new Date(mDateTakenInSeconds);
+ return "Photo:" + ",data=" + path + ",mimeType=" + mimeType
+ + "," + width + "x" + height + ",orientation=" + orientation
+ + ",date=" + new Date(dateTakenInSeconds);
}
@Override
@@ -404,20 +361,20 @@ public abstract class LocalMediaData implements LocalData {
@Override
public boolean delete(Context c) {
ContentResolver cr = c.getContentResolver();
- cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + mId, null);
+ cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + id, null);
return super.delete(c);
}
@Override
public Uri getContentUri() {
Uri baseUri = CONTENT_URI;
- return baseUri.buildUpon().appendPath(String.valueOf(mId)).build();
+ return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
}
@Override
public MediaDetails getMediaDetails(Context context) {
MediaDetails mediaDetails = super.getMediaDetails(context);
- MediaDetails.extractExifInfo(mediaDetails, mPath);
+ MediaDetails.extractExifInfo(mediaDetails, path);
return mediaDetails;
}
@@ -430,14 +387,23 @@ public abstract class LocalMediaData implements LocalData {
}
@Override
- public LocalData refresh(ContentResolver resolver) {
+ public boolean refresh(ContentResolver resolver) {
Cursor c = resolver.query(
getContentUri(), QUERY_PROJECTION, null, null, null);
if (c == null || !c.moveToFirst()) {
- return null;
+ return false;
}
PhotoData newData = buildFromCursor(c);
- return newData;
+ id = newData.id;
+ title = newData.title;
+ mimeType = newData.mimeType;
+ dateTakenInSeconds = newData.dateTakenInSeconds;
+ dateModifiedInSeconds = newData.dateModifiedInSeconds;
+ path = newData.path;
+ orientation = newData.orientation;
+ width = newData.width;
+ height = newData.height;
+ return true;
}
@Override
@@ -464,9 +430,9 @@ public abstract class LocalMediaData implements LocalData {
@Override
protected Bitmap doInBackground(Void... v) {
int sampleSize = 1;
- if (mWidth > mDecodeWidth || mHeight > mDecodeHeight) {
- int heightRatio = Math.round((float) mHeight / (float) mDecodeHeight);
- int widthRatio = Math.round((float) mWidth / (float) mDecodeWidth);
+ if (width > mDecodeWidth || height > mDecodeHeight) {
+ int heightRatio = Math.round((float) height / (float) mDecodeHeight);
+ int widthRatio = Math.round((float) width / (float) mDecodeWidth);
sampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
@@ -476,30 +442,21 @@ public abstract class LocalMediaData implements LocalData {
if (isCancelled() || !isUsing()) {
return null;
}
- Bitmap b = BitmapFactory.decodeFile(mPath, opts);
- if (mOrientation != 0) {
+ Bitmap b = BitmapFactory.decodeFile(path, opts);
+ if (orientation != 0) {
if (isCancelled() || !isUsing()) {
return null;
}
Matrix m = new Matrix();
- m.setRotate(mOrientation);
+ m.setRotate(orientation);
b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
}
return b;
}
}
-
- @Override
- public void rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- RotationTask task = new RotationTask(context, adapter,
- currentDataId, clockwise);
- task.execute(this);
- return;
- }
}
- public static final class VideoData extends LocalMediaData {
+ public static class VideoData extends LocalMediaData {
public static final int COL_ID = 0;
public static final int COL_TITLE = 1;
public static final int COL_MIME_TYPE = 2;
@@ -544,31 +501,26 @@ public abstract class LocalMediaData implements LocalData {
MediaStore.Video.VideoColumns.DURATION // 12 long
};
- /** The duration in milliseconds. */
- private long mDurationInSeconds;
+ private Uri mPlayUri;
- public VideoData(long id, String title, String mimeType,
- long dateTakenInSeconds, long dateModifiedInSeconds,
- String path, int width, int height, long sizeInBytes,
- double latitude, double longitude, long durationInSeconds) {
- super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
- path, width, height, sizeInBytes, latitude, longitude);
- mDurationInSeconds = durationInSeconds;
- }
+ /** The duration in milliseconds. */
+ private long durationInSeconds;
static VideoData buildFromCursor(Cursor c) {
- long id = c.getLong(COL_ID);
- String title = c.getString(COL_TITLE);
- String mimeType = c.getString(COL_MIME_TYPE);
- long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
- long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
- String path = c.getString(COL_DATA);
- int width = c.getInt(COL_WIDTH);
- int height = c.getInt(COL_HEIGHT);
+ VideoData d = new VideoData();
+ d.id = c.getLong(COL_ID);
+ d.title = c.getString(COL_TITLE);
+ d.mimeType = c.getString(COL_MIME_TYPE);
+ d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
+ d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
+ d.path = c.getString(COL_DATA);
+ d.width = c.getInt(COL_WIDTH);
+ d.height = c.getInt(COL_HEIGHT);
+ d.mPlayUri = d.getContentUri();
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
String rotation = null;
try {
- retriever.setDataSource(path);
+ retriever.setDataSource(d.path);
} catch (IllegalArgumentException ex) {
retriever.release();
Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:"
@@ -577,43 +529,33 @@ public abstract class LocalMediaData implements LocalData {
}
rotation = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
-
- // Extracts video height/width if available. If unavailable, set to 0.
- if (width == 0 || height == 0) {
- String val = retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
- width = (val == null) ? 0 : Integer.parseInt(val);
- val = retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
- height = (val == null) ? 0 : Integer.parseInt(val);
+ if (d.width == 0 || d.height == 0) {
+ retrieveVideoDimension(retriever, d);
}
retriever.release();
- if (width == 0 || height == 0) {
+ if (d.width == 0 || d.height == 0) {
// Width or height is still not available.
- Log.e(TAG, "Unable to retrieve dimension of video:" + path);
+ Log.e(TAG, "Unable to retrieve dimension of video:" + d.path);
return null;
}
if (rotation != null
&& (rotation.equals("90") || rotation.equals("270"))) {
- int b = width;
- width = height;
- height = b;
+ int b = d.width;
+ d.width = d.height;
+ d.height = b;
}
- long sizeInBytes = c.getLong(COL_SIZE);
- double latitude = c.getDouble(COL_LATITUDE);
- double longitude = c.getDouble(COL_LONGITUDE);
- long durationInSeconds = c.getLong(COL_DURATION) / 1000;
- VideoData d = new VideoData(id, title, mimeType, dateTakenInSeconds,
- dateModifiedInSeconds, path, width, height, sizeInBytes,
- latitude, longitude, durationInSeconds);
+ d.sizeInBytes = c.getLong(COL_SIZE);
+ d.latitude = c.getDouble(COL_LATITUDE);
+ d.longitude = c.getDouble(COL_LONGITUDE);
+ d.durationInSeconds = c.getLong(COL_DURATION) / 1000;
return d;
}
@Override
public String toString() {
- return "Video:" + ",data=" + mPath + ",mimeType=" + mMimeType
- + "," + mWidth + "x" + mHeight + ",date=" + new Date(mDateTakenInSeconds);
+ return "Video:" + ",data=" + path + ",mimeType=" + mimeType
+ + "," + width + "x" + height + ",date=" + new Date(dateTakenInSeconds);
}
@Override
@@ -634,20 +576,20 @@ public abstract class LocalMediaData implements LocalData {
@Override
public boolean delete(Context ctx) {
ContentResolver cr = ctx.getContentResolver();
- cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + mId, null);
+ cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + id, null);
return super.delete(ctx);
}
@Override
public Uri getContentUri() {
Uri baseUri = CONTENT_URI;
- return baseUri.buildUpon().appendPath(String.valueOf(mId)).build();
+ return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
}
@Override
public MediaDetails getMediaDetails(Context context) {
MediaDetails mediaDetails = super.getMediaDetails(context);
- String duration = MediaDetails.formatDuration(context, mDurationInSeconds);
+ String duration = MediaDetails.formatDuration(context, durationInSeconds);
mediaDetails.addDetail(MediaDetails.INDEX_DURATION, duration);
return mediaDetails;
}
@@ -658,14 +600,26 @@ public abstract class LocalMediaData implements LocalData {
}
@Override
- public LocalData refresh(ContentResolver resolver) {
+ public boolean refresh(ContentResolver resolver) {
Cursor c = resolver.query(
getContentUri(), QUERY_PROJECTION, null, null, null);
if (c == null || !c.moveToFirst()) {
- return null;
+ return false;
}
VideoData newData = buildFromCursor(c);
- return newData;
+ if (newData == null) {
+ return false;
+ }
+ id = newData.id;
+ title = newData.title;
+ mimeType = newData.mimeType;
+ dateTakenInSeconds = newData.dateTakenInSeconds;
+ dateModifiedInSeconds = newData.dateModifiedInSeconds;
+ path = newData.path;
+ width = newData.width;
+ height = newData.height;
+ mPlayUri = newData.mPlayUri;
+ return true;
}
@Override
@@ -689,7 +643,7 @@ public abstract class LocalMediaData implements LocalData {
icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- CameraUtil.playVideo(ctx, getContentUri(), mTitle);
+ CameraUtil.playVideo(ctx, mPlayUri, title);
}
});
@@ -722,7 +676,7 @@ public abstract class LocalMediaData implements LocalData {
return null;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- retriever.setDataSource(mPath);
+ retriever.setDataSource(path);
byte[] data = retriever.getEmbeddedPicture();
Bitmap bitmap = null;
if (isCancelled() || !isUsing()) {
@@ -740,12 +694,20 @@ public abstract class LocalMediaData implements LocalData {
}
}
- @Override
- public void rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- // We don't support rotation for video data.
- Log.e(TAG, "Unexpected call in rotate90Degrees()");
- return;
+ /**
+ * Extracts video height/width if available. If unavailable, set to 0.
+ *
+ * @param retriever An initialized metadata retriever.
+ * @param d The {@link VideoData} whose width/height are to update.
+ */
+ private static void retrieveVideoDimension(
+ MediaMetadataRetriever retriever, VideoData d) {
+ String val = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ d.width = (val == null) ? 0 : Integer.parseInt(val);
+ val = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ d.height = (val == null) ? 0 : Integer.parseInt(val);
}
}
@@ -767,7 +729,7 @@ public abstract class LocalMediaData implements LocalData {
return;
}
if (bitmap == null) {
- Log.e(TAG, "Failed decoding bitmap for file:" + mPath);
+ Log.e(TAG, "Failed decoding bitmap for file:" + path);
return;
}
BitmapDrawable d = new BitmapDrawable(bitmap);
diff --git a/src/com/android/camera/data/MediaDetails.java b/src/com/android/camera/data/MediaDetails.java
index dd4c147cc..a614b8d84 100644
--- a/src/com/android/camera/data/MediaDetails.java
+++ b/src/com/android/camera/data/MediaDetails.java
@@ -20,9 +20,9 @@ import android.content.Context;
import android.util.Log;
import android.util.SparseIntArray;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.exif.ExifTag;
import com.android.camera2.R;
+import com.android.gallery3d.exif.ExifInterface;
+import com.android.gallery3d.exif.ExifTag;
import java.io.FileNotFoundException;
import java.io.IOException;
diff --git a/src/com/android/camera/data/RotationTask.java b/src/com/android/camera/data/RotationTask.java
deleted file mode 100644
index 8daead156..000000000
--- a/src/com/android/camera/data/RotationTask.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.data;
-
-import android.app.ProgressDialog;
-import android.content.ContentValues;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.provider.MediaStore.Images;
-import android.util.Log;
-
-import com.android.camera.data.LocalMediaData.PhotoData;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.exif.ExifTag;
-import com.android.camera2.R;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-/**
- * RotationTask can be used to rotate a {@link LocalData} by updating the exif
- * data from jpeg file. Note that only {@link PhotoData} can be rotated.
- */
-public class RotationTask extends AsyncTask<LocalData, Void, LocalData> {
- private static final String TAG = "CAM_RotationTask";
- private final Context mContext;
- private final LocalDataAdapter mAdapter;
- private final int mCurrentDataId;
- private final boolean mClockwise;
- private ProgressDialog mProgress;
-
- public RotationTask(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- mContext = context;
- mAdapter = adapter;
- mCurrentDataId = currentDataId;
- mClockwise = clockwise;
- }
-
- @Override
- protected void onPreExecute() {
- // Show a progress bar since the rotation could take long.
- mProgress = new ProgressDialog(mContext);
- int titleStringId = mClockwise ? R.string.rotate_right : R.string.rotate_left;
- mProgress.setTitle(mContext.getString(titleStringId));
- mProgress.setMessage(mContext.getString(R.string.please_wait));
- mProgress.setCancelable(false);
- mProgress.show();
- }
-
- @Override
- protected LocalData doInBackground(LocalData... data) {
- return rotateInJpegExif(data[0]);
- }
-
- /**
- * Rotates the image by updating the exif. Done in background thread.
- * The worst case is the whole file needed to be re-written with
- * modified exif data.
- *
- * @return A new {@link LocalData} object which containing the new info.
- */
- private LocalData rotateInJpegExif(LocalData data) {
- if (!(data instanceof PhotoData)) {
- Log.w(TAG, "Rotation can only happen on PhotoData.");
- return null;
- }
-
- PhotoData imageData = (PhotoData) data;
- int originRotation = imageData.getOrientation();
- int finalRotationDegrees;
- if (mClockwise) {
- finalRotationDegrees = (originRotation + 90) % 360;
- } else {
- finalRotationDegrees = (originRotation + 270) % 360;
- }
-
- String filePath = imageData.getPath();
- ContentValues values = new ContentValues();
- boolean success = false;
- int newOrientation = 0;
- if (imageData.getMimeType().equalsIgnoreCase(LocalData.MIME_TYPE_JPEG)) {
- ExifInterface exifInterface = new ExifInterface();
- ExifTag tag = exifInterface.buildTag(
- ExifInterface.TAG_ORIENTATION,
- ExifInterface.getOrientationValueForRotation(
- finalRotationDegrees));
- if (tag != null) {
- exifInterface.setTag(tag);
- try {
- exifInterface.forceRewriteExif(filePath);
- long fileSize = new File(filePath).length();
- values.put(Images.Media.SIZE, fileSize);
- newOrientation = finalRotationDegrees;
- success = true;
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Cannot find file to set exif: " + filePath);
- } catch (IOException e) {
- Log.w(TAG, "Cannot set exif data: " + filePath);
- }
- } else {
- Log.w(TAG, "Cannot build tag: " + ExifInterface.TAG_ORIENTATION);
- }
- }
-
- PhotoData result = null;
- if (success) {
- // Swap width and height after rotation.
- int oldWidth = imageData.getWidth();
- int newWidth = imageData.getHeight();
- int newHeight = oldWidth;
- values.put(Images.Media.WIDTH, newWidth);
- values.put(Images.Media.HEIGHT, newHeight);
-
- // MediaStore using SQLite is thread safe.
- values.put(Images.Media.ORIENTATION, finalRotationDegrees);
- mContext.getContentResolver().update(imageData.getContentUri(),
- values, "_id=?",
- new String[] {
- String.valueOf(imageData.getId()) });
-
- double[] latLong = data.getLatLong();
- double latitude = 0;
- double longitude = 0;
- if (latLong != null) {
- latitude = latLong[0];
- longitude = latLong[1];
- }
-
- result = new PhotoData(data.getId(), data.getTitle(),
- data.getMimeType(), data.getDateTaken(), data.getDateModified(),
- data.getPath(), newOrientation, newWidth, newHeight,
- data.getSizeInBytes(), latitude, longitude);
- }
-
- return result;
- }
-
- @Override
- protected void onPostExecute(LocalData result) {
- mProgress.dismiss();
- if (result != null) {
- mAdapter.updateData(mCurrentDataId, result);
- }
- }
-}
diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java
index a49d3eac8..06ff3501b 100644
--- a/src/com/android/camera/data/SimpleViewData.java
+++ b/src/com/android/camera/data/SimpleViewData.java
@@ -20,7 +20,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.util.Log;
import android.view.View;
import com.android.camera.ui.FilmStripView;
@@ -30,13 +29,11 @@ import com.android.camera.util.PhotoSphereHelper;
* A LocalData that does nothing but only shows a view.
*/
public class SimpleViewData implements LocalData {
- private static final String TAG = "CAM_SimpleViewData";
-
- private final int mWidth;
- private final int mHeight;
- private final View mView;
- private final long mDateTaken;
- private final long mDateModified;
+ private int mWidth;
+ private int mHeight;
+ private View mView;
+ private long mDateTaken;
+ private long mDateModified;
public SimpleViewData(
View v, int width, int height,
@@ -94,8 +91,8 @@ public class SimpleViewData implements LocalData {
}
@Override
- public LocalData refresh(ContentResolver resolver) {
- return null;
+ public boolean refresh(ContentResolver resolver) {
+ return false;
}
@Override
@@ -168,22 +165,4 @@ public class SimpleViewData implements LocalData {
public String getMimeType() {
return null;
}
-
- @Override
- public void rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- // We don't support rotation for SimpleViewData.
- Log.w(TAG, "Unexpected call in rotate90Degrees()");
- return;
- }
-
- @Override
- public long getSizeInBytes() {
- return 0;
- }
-
- @Override
- public long getId() {
- return -1;
- }
}
diff --git a/src/com/android/camera/exif/ByteBufferInputStream.java b/src/com/android/camera/exif/ByteBufferInputStream.java
deleted file mode 100644
index 77c846ad0..000000000
--- a/src/com/android/camera/exif/ByteBufferInputStream.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-class ByteBufferInputStream extends InputStream {
-
- private ByteBuffer mBuf;
-
- public ByteBufferInputStream(ByteBuffer buf) {
- mBuf = buf;
- }
-
- @Override
- public int read() {
- if (!mBuf.hasRemaining()) {
- return -1;
- }
- return mBuf.get() & 0xFF;
- }
-
- @Override
- public int read(byte[] bytes, int off, int len) {
- if (!mBuf.hasRemaining()) {
- return -1;
- }
-
- len = Math.min(len, mBuf.remaining());
- mBuf.get(bytes, off, len);
- return len;
- }
-}
diff --git a/src/com/android/camera/exif/CountedDataInputStream.java b/src/com/android/camera/exif/CountedDataInputStream.java
deleted file mode 100644
index 4232e7d99..000000000
--- a/src/com/android/camera/exif/CountedDataInputStream.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import java.io.EOFException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-
-class CountedDataInputStream extends FilterInputStream {
-
- private int mCount = 0;
-
- // allocate a byte buffer for a long value;
- private final byte mByteArray[] = new byte[8];
- private final ByteBuffer mByteBuffer = ByteBuffer.wrap(mByteArray);
-
- protected CountedDataInputStream(InputStream in) {
- super(in);
- }
-
- public int getReadByteCount() {
- return mCount;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- int r = in.read(b);
- mCount += (r >= 0) ? r : 0;
- return r;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int r = in.read(b, off, len);
- mCount += (r >= 0) ? r : 0;
- return r;
- }
-
- @Override
- public int read() throws IOException {
- int r = in.read();
- mCount += (r >= 0) ? 1 : 0;
- return r;
- }
-
- @Override
- public long skip(long length) throws IOException {
- long skip = in.skip(length);
- mCount += skip;
- return skip;
- }
-
- public void skipOrThrow(long length) throws IOException {
- if (skip(length) != length) throw new EOFException();
- }
-
- public void skipTo(long target) throws IOException {
- long cur = mCount;
- long diff = target - cur;
- assert(diff >= 0);
- skipOrThrow(diff);
- }
-
- public void readOrThrow(byte[] b, int off, int len) throws IOException {
- int r = read(b, off, len);
- if (r != len) throw new EOFException();
- }
-
- public void readOrThrow(byte[] b) throws IOException {
- readOrThrow(b, 0, b.length);
- }
-
- public void setByteOrder(ByteOrder order) {
- mByteBuffer.order(order);
- }
-
- public ByteOrder getByteOrder() {
- return mByteBuffer.order();
- }
-
- public short readShort() throws IOException {
- readOrThrow(mByteArray, 0 ,2);
- mByteBuffer.rewind();
- return mByteBuffer.getShort();
- }
-
- public int readUnsignedShort() throws IOException {
- return readShort() & 0xffff;
- }
-
- public int readInt() throws IOException {
- readOrThrow(mByteArray, 0 , 4);
- mByteBuffer.rewind();
- return mByteBuffer.getInt();
- }
-
- public long readUnsignedInt() throws IOException {
- return readInt() & 0xffffffffL;
- }
-
- public long readLong() throws IOException {
- readOrThrow(mByteArray, 0 , 8);
- mByteBuffer.rewind();
- return mByteBuffer.getLong();
- }
-
- public String readString(int n) throws IOException {
- byte buf[] = new byte[n];
- readOrThrow(buf);
- return new String(buf, "UTF8");
- }
-
- public String readString(int n, Charset charset) throws IOException {
- byte buf[] = new byte[n];
- readOrThrow(buf);
- return new String(buf, charset);
- }
-} \ No newline at end of file
diff --git a/src/com/android/camera/exif/ExifData.java b/src/com/android/camera/exif/ExifData.java
deleted file mode 100644
index ed1f3499a..000000000
--- a/src/com/android/camera/exif/ExifData.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * This class stores the EXIF header in IFDs according to the JPEG
- * specification. It is the result produced by {@link ExifReader}.
- *
- * @see ExifReader
- * @see IfdData
- */
-class ExifData {
- private static final String TAG = "ExifData";
- private static final byte[] USER_COMMENT_ASCII = {
- 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00
- };
- private static final byte[] USER_COMMENT_JIS = {
- 0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- private static final byte[] USER_COMMENT_UNICODE = {
- 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00
- };
-
- private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
- private byte[] mThumbnail;
- private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
- private final ByteOrder mByteOrder;
-
- ExifData(ByteOrder order) {
- mByteOrder = order;
- }
-
- /**
- * Gets the compressed thumbnail. Returns null if there is no compressed
- * thumbnail.
- *
- * @see #hasCompressedThumbnail()
- */
- protected byte[] getCompressedThumbnail() {
- return mThumbnail;
- }
-
- /**
- * Sets the compressed thumbnail.
- */
- protected void setCompressedThumbnail(byte[] thumbnail) {
- mThumbnail = thumbnail;
- }
-
- /**
- * Returns true it this header contains a compressed thumbnail.
- */
- protected boolean hasCompressedThumbnail() {
- return mThumbnail != null;
- }
-
- /**
- * Adds an uncompressed strip.
- */
- protected void setStripBytes(int index, byte[] strip) {
- if (index < mStripBytes.size()) {
- mStripBytes.set(index, strip);
- } else {
- for (int i = mStripBytes.size(); i < index; i++) {
- mStripBytes.add(null);
- }
- mStripBytes.add(strip);
- }
- }
-
- /**
- * Gets the strip count.
- */
- protected int getStripCount() {
- return mStripBytes.size();
- }
-
- /**
- * Gets the strip at the specified index.
- *
- * @exceptions #IndexOutOfBoundException
- */
- protected byte[] getStrip(int index) {
- return mStripBytes.get(index);
- }
-
- /**
- * Returns true if this header contains uncompressed strip.
- */
- protected boolean hasUncompressedStrip() {
- return mStripBytes.size() != 0;
- }
-
- /**
- * Gets the byte order.
- */
- protected ByteOrder getByteOrder() {
- return mByteOrder;
- }
-
- /**
- * Returns the {@link IfdData} object corresponding to a given IFD if it
- * exists or null.
- */
- protected IfdData getIfdData(int ifdId) {
- if (ExifTag.isValidIfd(ifdId)) {
- return mIfdDatas[ifdId];
- }
- return null;
- }
-
- /**
- * Adds IFD data. If IFD data of the same type already exists, it will be
- * replaced by the new data.
- */
- protected void addIfdData(IfdData data) {
- mIfdDatas[data.getId()] = data;
- }
-
- /**
- * Returns the {@link IfdData} object corresponding to a given IFD or
- * generates one if none exist.
- */
- protected IfdData getOrCreateIfdData(int ifdId) {
- IfdData ifdData = mIfdDatas[ifdId];
- if (ifdData == null) {
- ifdData = new IfdData(ifdId);
- mIfdDatas[ifdId] = ifdData;
- }
- return ifdData;
- }
-
- /**
- * Returns the tag with a given TID in the given IFD if the tag exists.
- * Otherwise returns null.
- */
- protected ExifTag getTag(short tag, int ifd) {
- IfdData ifdData = mIfdDatas[ifd];
- return (ifdData == null) ? null : ifdData.getTag(tag);
- }
-
- /**
- * Adds the given ExifTag to its default IFD and returns an existing ExifTag
- * with the same TID or null if none exist.
- */
- protected ExifTag addTag(ExifTag tag) {
- if (tag != null) {
- int ifd = tag.getIfd();
- return addTag(tag, ifd);
- }
- return null;
- }
-
- /**
- * Adds the given ExifTag to the given IFD and returns an existing ExifTag
- * with the same TID or null if none exist.
- */
- protected ExifTag addTag(ExifTag tag, int ifdId) {
- if (tag != null && ExifTag.isValidIfd(ifdId)) {
- IfdData ifdData = getOrCreateIfdData(ifdId);
- return ifdData.setTag(tag);
- }
- return null;
- }
-
- protected void clearThumbnailAndStrips() {
- mThumbnail = null;
- mStripBytes.clear();
- }
-
- /**
- * Removes the thumbnail and its related tags. IFD1 will be removed.
- */
- protected void removeThumbnailData() {
- clearThumbnailAndStrips();
- mIfdDatas[IfdId.TYPE_IFD_1] = null;
- }
-
- /**
- * Removes the tag with a given TID and IFD.
- */
- protected void removeTag(short tagId, int ifdId) {
- IfdData ifdData = mIfdDatas[ifdId];
- if (ifdData == null) {
- return;
- }
- ifdData.removeTag(tagId);
- }
-
- /**
- * Decodes the user comment tag into string as specified in the EXIF
- * standard. Returns null if decoding failed.
- */
- protected String getUserComment() {
- IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_0];
- if (ifdData == null) {
- return null;
- }
- ExifTag tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT));
- if (tag == null) {
- return null;
- }
- if (tag.getComponentCount() < 8) {
- return null;
- }
-
- byte[] buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
-
- byte[] code = new byte[8];
- System.arraycopy(buf, 0, code, 0, 8);
-
- try {
- if (Arrays.equals(code, USER_COMMENT_ASCII)) {
- return new String(buf, 8, buf.length - 8, "US-ASCII");
- } else if (Arrays.equals(code, USER_COMMENT_JIS)) {
- return new String(buf, 8, buf.length - 8, "EUC-JP");
- } else if (Arrays.equals(code, USER_COMMENT_UNICODE)) {
- return new String(buf, 8, buf.length - 8, "UTF-16");
- } else {
- return null;
- }
- } catch (UnsupportedEncodingException e) {
- Log.w(TAG, "Failed to decode the user comment");
- return null;
- }
- }
-
- /**
- * Returns a list of all {@link ExifTag}s in the ExifData or null if there
- * are none.
- */
- protected List<ExifTag> getAllTags() {
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
- for (IfdData d : mIfdDatas) {
- if (d != null) {
- ExifTag[] tags = d.getAllTags();
- if (tags != null) {
- for (ExifTag t : tags) {
- ret.add(t);
- }
- }
- }
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- /**
- * Returns a list of all {@link ExifTag}s in a given IFD or null if there
- * are none.
- */
- protected List<ExifTag> getAllTagsForIfd(int ifd) {
- IfdData d = mIfdDatas[ifd];
- if (d == null) {
- return null;
- }
- ExifTag[] tags = d.getAllTags();
- if (tags == null) {
- return null;
- }
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>(tags.length);
- for (ExifTag t : tags) {
- ret.add(t);
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- /**
- * Returns a list of all {@link ExifTag}s with a given TID or null if there
- * are none.
- */
- protected List<ExifTag> getAllTagsForTagId(short tag) {
- ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
- for (IfdData d : mIfdDatas) {
- if (d != null) {
- ExifTag t = d.getTag(tag);
- if (t != null) {
- ret.add(t);
- }
- }
- }
- if (ret.size() == 0) {
- return null;
- }
- return ret;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (obj instanceof ExifData) {
- ExifData data = (ExifData) obj;
- if (data.mByteOrder != mByteOrder ||
- data.mStripBytes.size() != mStripBytes.size() ||
- !Arrays.equals(data.mThumbnail, mThumbnail)) {
- return false;
- }
- for (int i = 0; i < mStripBytes.size(); i++) {
- if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) {
- return false;
- }
- }
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- IfdData ifd1 = data.getIfdData(i);
- IfdData ifd2 = getIfdData(i);
- if (ifd1 != ifd2 && ifd1 != null && !ifd1.equals(ifd2)) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
-}
diff --git a/src/com/android/camera/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java
deleted file mode 100644
index 340f19e1c..000000000
--- a/src/com/android/camera/exif/ExifInterface.java
+++ /dev/null
@@ -1,2407 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.util.SparseIntArray;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel.MapMode;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.TimeZone;
-
-/**
- * This class provides methods and constants for reading and writing jpeg file
- * metadata. It contains a collection of ExifTags, and a collection of
- * definitions for creating valid ExifTags. The collection of ExifTags can be
- * updated by: reading new ones from a file, deleting or adding existing ones,
- * or building new ExifTags from a tag definition. These ExifTags can be written
- * to a valid jpeg image as exif metadata.
- * <p>
- * Each ExifTag has a tag ID (TID) and is stored in a specific image file
- * directory (IFD) as specified by the exif standard. A tag definition can be
- * looked up with a constant that is a combination of TID and IFD. This
- * definition has information about the type, number of components, and valid
- * IFDs for a tag.
- *
- * @see ExifTag
- */
-public class ExifInterface {
- public static final int TAG_NULL = -1;
- public static final int IFD_NULL = -1;
- public static final int DEFINITION_NULL = 0;
-
- /**
- * Tag constants for Jeita EXIF 2.2
- */
-
- // IFD 0
- public static final int TAG_IMAGE_WIDTH =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0100);
- public static final int TAG_IMAGE_LENGTH =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0101); // Image height
- public static final int TAG_BITS_PER_SAMPLE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0102);
- public static final int TAG_COMPRESSION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0103);
- public static final int TAG_PHOTOMETRIC_INTERPRETATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0106);
- public static final int TAG_IMAGE_DESCRIPTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x010E);
- public static final int TAG_MAKE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x010F);
- public static final int TAG_MODEL =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0110);
- public static final int TAG_STRIP_OFFSETS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0111);
- public static final int TAG_ORIENTATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0112);
- public static final int TAG_SAMPLES_PER_PIXEL =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0115);
- public static final int TAG_ROWS_PER_STRIP =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0116);
- public static final int TAG_STRIP_BYTE_COUNTS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0117);
- public static final int TAG_X_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011A);
- public static final int TAG_Y_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011B);
- public static final int TAG_PLANAR_CONFIGURATION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x011C);
- public static final int TAG_RESOLUTION_UNIT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0128);
- public static final int TAG_TRANSFER_FUNCTION =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x012D);
- public static final int TAG_SOFTWARE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0131);
- public static final int TAG_DATE_TIME =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0132);
- public static final int TAG_ARTIST =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013B);
- public static final int TAG_WHITE_POINT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013E);
- public static final int TAG_PRIMARY_CHROMATICITIES =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x013F);
- public static final int TAG_Y_CB_CR_COEFFICIENTS =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0211);
- public static final int TAG_Y_CB_CR_SUB_SAMPLING =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0212);
- public static final int TAG_Y_CB_CR_POSITIONING =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0213);
- public static final int TAG_REFERENCE_BLACK_WHITE =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x0214);
- public static final int TAG_COPYRIGHT =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8298);
- public static final int TAG_EXIF_IFD =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8769);
- public static final int TAG_GPS_IFD =
- defineTag(IfdId.TYPE_IFD_0, (short) 0x8825);
- // IFD 1
- public static final int TAG_JPEG_INTERCHANGE_FORMAT =
- defineTag(IfdId.TYPE_IFD_1, (short) 0x0201);
- public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH =
- defineTag(IfdId.TYPE_IFD_1, (short) 0x0202);
- // IFD Exif Tags
- public static final int TAG_EXPOSURE_TIME =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829A);
- public static final int TAG_F_NUMBER =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829D);
- public static final int TAG_EXPOSURE_PROGRAM =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8822);
- public static final int TAG_SPECTRAL_SENSITIVITY =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8824);
- public static final int TAG_ISO_SPEED_RATINGS =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8827);
- public static final int TAG_OECF =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8828);
- public static final int TAG_EXIF_VERSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9000);
- public static final int TAG_DATE_TIME_ORIGINAL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9003);
- public static final int TAG_DATE_TIME_DIGITIZED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9004);
- public static final int TAG_COMPONENTS_CONFIGURATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9101);
- public static final int TAG_COMPRESSED_BITS_PER_PIXEL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9102);
- public static final int TAG_SHUTTER_SPEED_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9201);
- public static final int TAG_APERTURE_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9202);
- public static final int TAG_BRIGHTNESS_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9203);
- public static final int TAG_EXPOSURE_BIAS_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9204);
- public static final int TAG_MAX_APERTURE_VALUE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9205);
- public static final int TAG_SUBJECT_DISTANCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9206);
- public static final int TAG_METERING_MODE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9207);
- public static final int TAG_LIGHT_SOURCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9208);
- public static final int TAG_FLASH =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9209);
- public static final int TAG_FOCAL_LENGTH =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x920A);
- public static final int TAG_SUBJECT_AREA =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9214);
- public static final int TAG_MAKER_NOTE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x927C);
- public static final int TAG_USER_COMMENT =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9286);
- public static final int TAG_SUB_SEC_TIME =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9290);
- public static final int TAG_SUB_SEC_TIME_ORIGINAL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9291);
- public static final int TAG_SUB_SEC_TIME_DIGITIZED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9292);
- public static final int TAG_FLASHPIX_VERSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA000);
- public static final int TAG_COLOR_SPACE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA001);
- public static final int TAG_PIXEL_X_DIMENSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA002);
- public static final int TAG_PIXEL_Y_DIMENSION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA003);
- public static final int TAG_RELATED_SOUND_FILE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA004);
- public static final int TAG_INTEROPERABILITY_IFD =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA005);
- public static final int TAG_FLASH_ENERGY =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20B);
- public static final int TAG_SPATIAL_FREQUENCY_RESPONSE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20C);
- public static final int TAG_FOCAL_PLANE_X_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20E);
- public static final int TAG_FOCAL_PLANE_Y_RESOLUTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20F);
- public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA210);
- public static final int TAG_SUBJECT_LOCATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA214);
- public static final int TAG_EXPOSURE_INDEX =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA215);
- public static final int TAG_SENSING_METHOD =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA217);
- public static final int TAG_FILE_SOURCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA300);
- public static final int TAG_SCENE_TYPE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA301);
- public static final int TAG_CFA_PATTERN =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA302);
- public static final int TAG_CUSTOM_RENDERED =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA401);
- public static final int TAG_EXPOSURE_MODE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA402);
- public static final int TAG_WHITE_BALANCE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA403);
- public static final int TAG_DIGITAL_ZOOM_RATIO =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA404);
- public static final int TAG_FOCAL_LENGTH_IN_35_MM_FILE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA405);
- public static final int TAG_SCENE_CAPTURE_TYPE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA406);
- public static final int TAG_GAIN_CONTROL =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA407);
- public static final int TAG_CONTRAST =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA408);
- public static final int TAG_SATURATION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA409);
- public static final int TAG_SHARPNESS =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40A);
- public static final int TAG_DEVICE_SETTING_DESCRIPTION =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40B);
- public static final int TAG_SUBJECT_DISTANCE_RANGE =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40C);
- public static final int TAG_IMAGE_UNIQUE_ID =
- defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA420);
- // IFD GPS tags
- public static final int TAG_GPS_VERSION_ID =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 0);
- public static final int TAG_GPS_LATITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 1);
- public static final int TAG_GPS_LATITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 2);
- public static final int TAG_GPS_LONGITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 3);
- public static final int TAG_GPS_LONGITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 4);
- public static final int TAG_GPS_ALTITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 5);
- public static final int TAG_GPS_ALTITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 6);
- public static final int TAG_GPS_TIME_STAMP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 7);
- public static final int TAG_GPS_SATTELLITES =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 8);
- public static final int TAG_GPS_STATUS =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 9);
- public static final int TAG_GPS_MEASURE_MODE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 10);
- public static final int TAG_GPS_DOP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 11);
- public static final int TAG_GPS_SPEED_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 12);
- public static final int TAG_GPS_SPEED =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 13);
- public static final int TAG_GPS_TRACK_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 14);
- public static final int TAG_GPS_TRACK =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 15);
- public static final int TAG_GPS_IMG_DIRECTION_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 16);
- public static final int TAG_GPS_IMG_DIRECTION =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 17);
- public static final int TAG_GPS_MAP_DATUM =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 18);
- public static final int TAG_GPS_DEST_LATITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 19);
- public static final int TAG_GPS_DEST_LATITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 20);
- public static final int TAG_GPS_DEST_LONGITUDE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 21);
- public static final int TAG_GPS_DEST_LONGITUDE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 22);
- public static final int TAG_GPS_DEST_BEARING_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 23);
- public static final int TAG_GPS_DEST_BEARING =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 24);
- public static final int TAG_GPS_DEST_DISTANCE_REF =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 25);
- public static final int TAG_GPS_DEST_DISTANCE =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 26);
- public static final int TAG_GPS_PROCESSING_METHOD =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 27);
- public static final int TAG_GPS_AREA_INFORMATION =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 28);
- public static final int TAG_GPS_DATE_STAMP =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 29);
- public static final int TAG_GPS_DIFFERENTIAL =
- defineTag(IfdId.TYPE_IFD_GPS, (short) 30);
- // IFD Interoperability tags
- public static final int TAG_INTEROPERABILITY_INDEX =
- defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1);
-
- /**
- * Tags that contain offset markers. These are included in the banned
- * defines.
- */
- private static HashSet<Short> sOffsetTags = new HashSet<Short>();
- static {
- sOffsetTags.add(getTrueTagKey(TAG_GPS_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_EXIF_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT));
- sOffsetTags.add(getTrueTagKey(TAG_INTEROPERABILITY_IFD));
- sOffsetTags.add(getTrueTagKey(TAG_STRIP_OFFSETS));
- }
-
- /**
- * Tags with definitions that cannot be overridden (banned defines).
- */
- protected static HashSet<Short> sBannedDefines = new HashSet<Short>(sOffsetTags);
- static {
- sBannedDefines.add(getTrueTagKey(TAG_NULL));
- sBannedDefines.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- sBannedDefines.add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS));
- }
-
- /**
- * Returns the constant representing a tag with a given TID and default IFD.
- */
- public static int defineTag(int ifdId, short tagId) {
- return (tagId & 0x0000ffff) | (ifdId << 16);
- }
-
- /**
- * Returns the TID for a tag constant.
- */
- public static short getTrueTagKey(int tag) {
- // Truncate
- return (short) tag;
- }
-
- /**
- * Returns the default IFD for a tag constant.
- */
- public static int getTrueIfd(int tag) {
- return tag >>> 16;
- }
-
- /**
- * Constants for {@link TAG_ORIENTATION}. They can be interpreted as
- * follows:
- * <ul>
- * <li>TOP_LEFT is the normal orientation.</li>
- * <li>TOP_RIGHT is a left-right mirror.</li>
- * <li>BOTTOM_LEFT is a 180 degree rotation.</li>
- * <li>BOTTOM_RIGHT is a top-bottom mirror.</li>
- * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.</li>
- * <li>RIGHT_TOP is a 90 degree clockwise rotation.</li>
- * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.</li>
- * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.</li>
- * </ul>
- */
- public static interface Orientation {
- public static final short TOP_LEFT = 1;
- public static final short TOP_RIGHT = 2;
- public static final short BOTTOM_LEFT = 3;
- public static final short BOTTOM_RIGHT = 4;
- public static final short LEFT_TOP = 5;
- public static final short RIGHT_TOP = 6;
- public static final short LEFT_BOTTOM = 7;
- public static final short RIGHT_BOTTOM = 8;
- }
-
- /**
- * Constants for {@link TAG_Y_CB_CR_POSITIONING}
- */
- public static interface YCbCrPositioning {
- public static final short CENTERED = 1;
- public static final short CO_SITED = 2;
- }
-
- /**
- * Constants for {@link TAG_COMPRESSION}
- */
- public static interface Compression {
- public static final short UNCOMPRESSION = 1;
- public static final short JPEG = 6;
- }
-
- /**
- * Constants for {@link TAG_RESOLUTION_UNIT}
- */
- public static interface ResolutionUnit {
- public static final short INCHES = 2;
- public static final short CENTIMETERS = 3;
- }
-
- /**
- * Constants for {@link TAG_PHOTOMETRIC_INTERPRETATION}
- */
- public static interface PhotometricInterpretation {
- public static final short RGB = 2;
- public static final short YCBCR = 6;
- }
-
- /**
- * Constants for {@link TAG_PLANAR_CONFIGURATION}
- */
- public static interface PlanarConfiguration {
- public static final short CHUNKY = 1;
- public static final short PLANAR = 2;
- }
-
- /**
- * Constants for {@link TAG_EXPOSURE_PROGRAM}
- */
- public static interface ExposureProgram {
- public static final short NOT_DEFINED = 0;
- public static final short MANUAL = 1;
- public static final short NORMAL_PROGRAM = 2;
- public static final short APERTURE_PRIORITY = 3;
- public static final short SHUTTER_PRIORITY = 4;
- public static final short CREATIVE_PROGRAM = 5;
- public static final short ACTION_PROGRAM = 6;
- public static final short PROTRAIT_MODE = 7;
- public static final short LANDSCAPE_MODE = 8;
- }
-
- /**
- * Constants for {@link TAG_METERING_MODE}
- */
- public static interface MeteringMode {
- public static final short UNKNOWN = 0;
- public static final short AVERAGE = 1;
- public static final short CENTER_WEIGHTED_AVERAGE = 2;
- public static final short SPOT = 3;
- public static final short MULTISPOT = 4;
- public static final short PATTERN = 5;
- public static final short PARTAIL = 6;
- public static final short OTHER = 255;
- }
-
- /**
- * Constants for {@link TAG_FLASH} As the definition in Jeita EXIF 2.2
- * standard, we can treat this constant as bitwise flag.
- * <p>
- * e.g.
- * <p>
- * short flash = FIRED | RETURN_STROBE_RETURN_LIGHT_DETECTED |
- * MODE_AUTO_MODE
- */
- public static interface Flash {
- // LSB
- public static final short DID_NOT_FIRED = 0;
- public static final short FIRED = 1;
- // 1st~2nd bits
- public static final short RETURN_NO_STROBE_RETURN_DETECTION_FUNCTION = 0 << 1;
- public static final short RETURN_STROBE_RETURN_LIGHT_NOT_DETECTED = 2 << 1;
- public static final short RETURN_STROBE_RETURN_LIGHT_DETECTED = 3 << 1;
- // 3rd~4th bits
- public static final short MODE_UNKNOWN = 0 << 3;
- public static final short MODE_COMPULSORY_FLASH_FIRING = 1 << 3;
- public static final short MODE_COMPULSORY_FLASH_SUPPRESSION = 2 << 3;
- public static final short MODE_AUTO_MODE = 3 << 3;
- // 5th bit
- public static final short FUNCTION_PRESENT = 0 << 5;
- public static final short FUNCTION_NO_FUNCTION = 1 << 5;
- // 6th bit
- public static final short RED_EYE_REDUCTION_NO_OR_UNKNOWN = 0 << 6;
- public static final short RED_EYE_REDUCTION_SUPPORT = 1 << 6;
- }
-
- /**
- * Constants for {@link TAG_COLOR_SPACE}
- */
- public static interface ColorSpace {
- public static final short SRGB = 1;
- public static final short UNCALIBRATED = (short) 0xFFFF;
- }
-
- /**
- * Constants for {@link TAG_EXPOSURE_MODE}
- */
- public static interface ExposureMode {
- public static final short AUTO_EXPOSURE = 0;
- public static final short MANUAL_EXPOSURE = 1;
- public static final short AUTO_BRACKET = 2;
- }
-
- /**
- * Constants for {@link TAG_WHITE_BALANCE}
- */
- public static interface WhiteBalance {
- public static final short AUTO = 0;
- public static final short MANUAL = 1;
- }
-
- /**
- * Constants for {@link TAG_SCENE_CAPTURE_TYPE}
- */
- public static interface SceneCapture {
- public static final short STANDARD = 0;
- public static final short LANDSCAPE = 1;
- public static final short PROTRAIT = 2;
- public static final short NIGHT_SCENE = 3;
- }
-
- /**
- * Constants for {@link TAG_COMPONENTS_CONFIGURATION}
- */
- public static interface ComponentsConfiguration {
- public static final short NOT_EXIST = 0;
- public static final short Y = 1;
- public static final short CB = 2;
- public static final short CR = 3;
- public static final short R = 4;
- public static final short G = 5;
- public static final short B = 6;
- }
-
- /**
- * Constants for {@link TAG_LIGHT_SOURCE}
- */
- public static interface LightSource {
- public static final short UNKNOWN = 0;
- public static final short DAYLIGHT = 1;
- public static final short FLUORESCENT = 2;
- public static final short TUNGSTEN = 3;
- public static final short FLASH = 4;
- public static final short FINE_WEATHER = 9;
- public static final short CLOUDY_WEATHER = 10;
- public static final short SHADE = 11;
- public static final short DAYLIGHT_FLUORESCENT = 12;
- public static final short DAY_WHITE_FLUORESCENT = 13;
- public static final short COOL_WHITE_FLUORESCENT = 14;
- public static final short WHITE_FLUORESCENT = 15;
- public static final short STANDARD_LIGHT_A = 17;
- public static final short STANDARD_LIGHT_B = 18;
- public static final short STANDARD_LIGHT_C = 19;
- public static final short D55 = 20;
- public static final short D65 = 21;
- public static final short D75 = 22;
- public static final short D50 = 23;
- public static final short ISO_STUDIO_TUNGSTEN = 24;
- public static final short OTHER = 255;
- }
-
- /**
- * Constants for {@link TAG_SENSING_METHOD}
- */
- public static interface SensingMethod {
- public static final short NOT_DEFINED = 1;
- public static final short ONE_CHIP_COLOR = 2;
- public static final short TWO_CHIP_COLOR = 3;
- public static final short THREE_CHIP_COLOR = 4;
- public static final short COLOR_SEQUENTIAL_AREA = 5;
- public static final short TRILINEAR = 7;
- public static final short COLOR_SEQUENTIAL_LINEAR = 8;
- }
-
- /**
- * Constants for {@link TAG_FILE_SOURCE}
- */
- public static interface FileSource {
- public static final short DSC = 3;
- }
-
- /**
- * Constants for {@link TAG_SCENE_TYPE}
- */
- public static interface SceneType {
- public static final short DIRECT_PHOTOGRAPHED = 1;
- }
-
- /**
- * Constants for {@link TAG_GAIN_CONTROL}
- */
- public static interface GainControl {
- public static final short NONE = 0;
- public static final short LOW_UP = 1;
- public static final short HIGH_UP = 2;
- public static final short LOW_DOWN = 3;
- public static final short HIGH_DOWN = 4;
- }
-
- /**
- * Constants for {@link TAG_CONTRAST}
- */
- public static interface Contrast {
- public static final short NORMAL = 0;
- public static final short SOFT = 1;
- public static final short HARD = 2;
- }
-
- /**
- * Constants for {@link TAG_SATURATION}
- */
- public static interface Saturation {
- public static final short NORMAL = 0;
- public static final short LOW = 1;
- public static final short HIGH = 2;
- }
-
- /**
- * Constants for {@link TAG_SHARPNESS}
- */
- public static interface Sharpness {
- public static final short NORMAL = 0;
- public static final short SOFT = 1;
- public static final short HARD = 2;
- }
-
- /**
- * Constants for {@link TAG_SUBJECT_DISTANCE}
- */
- public static interface SubjectDistance {
- public static final short UNKNOWN = 0;
- public static final short MACRO = 1;
- public static final short CLOSE_VIEW = 2;
- public static final short DISTANT_VIEW = 3;
- }
-
- /**
- * Constants for {@link TAG_GPS_LATITUDE_REF},
- * {@link TAG_GPS_DEST_LATITUDE_REF}
- */
- public static interface GpsLatitudeRef {
- public static final String NORTH = "N";
- public static final String SOUTH = "S";
- }
-
- /**
- * Constants for {@link TAG_GPS_LONGITUDE_REF},
- * {@link TAG_GPS_DEST_LONGITUDE_REF}
- */
- public static interface GpsLongitudeRef {
- public static final String EAST = "E";
- public static final String WEST = "W";
- }
-
- /**
- * Constants for {@link TAG_GPS_ALTITUDE_REF}
- */
- public static interface GpsAltitudeRef {
- public static final short SEA_LEVEL = 0;
- public static final short SEA_LEVEL_NEGATIVE = 1;
- }
-
- /**
- * Constants for {@link TAG_GPS_STATUS}
- */
- public static interface GpsStatus {
- public static final String IN_PROGRESS = "A";
- public static final String INTEROPERABILITY = "V";
- }
-
- /**
- * Constants for {@link TAG_GPS_MEASURE_MODE}
- */
- public static interface GpsMeasureMode {
- public static final String MODE_2_DIMENSIONAL = "2";
- public static final String MODE_3_DIMENSIONAL = "3";
- }
-
- /**
- * Constants for {@link TAG_GPS_SPEED_REF},
- * {@link TAG_GPS_DEST_DISTANCE_REF}
- */
- public static interface GpsSpeedRef {
- public static final String KILOMETERS = "K";
- public static final String MILES = "M";
- public static final String KNOTS = "N";
- }
-
- /**
- * Constants for {@link TAG_GPS_TRACK_REF},
- * {@link TAG_GPS_IMG_DIRECTION_REF}, {@link TAG_GPS_DEST_BEARING_REF}
- */
- public static interface GpsTrackRef {
- public static final String TRUE_DIRECTION = "T";
- public static final String MAGNETIC_DIRECTION = "M";
- }
-
- /**
- * Constants for {@link TAG_GPS_DIFFERENTIAL}
- */
- public static interface GpsDifferential {
- public static final short WITHOUT_DIFFERENTIAL_CORRECTION = 0;
- public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1;
- }
-
- private static final String NULL_ARGUMENT_STRING = "Argument is null";
- private ExifData mData = new ExifData(DEFAULT_BYTE_ORDER);
- public static final ByteOrder DEFAULT_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
-
- public ExifInterface() {
- mGPSDateStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- /**
- * Reads the exif tags from a byte array, clearing this ExifInterface
- * object's existing exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @throws IOException
- */
- public void readExif(byte[] jpeg) throws IOException {
- readExif(new ByteArrayInputStream(jpeg));
- }
-
- /**
- * Reads the exif tags from an InputStream, clearing this ExifInterface
- * object's existing exif tags.
- *
- * @param inStream an InputStream containing a jpeg compressed image.
- * @throws IOException
- */
- public void readExif(InputStream inStream) throws IOException {
- if (inStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- ExifData d = null;
- try {
- d = new ExifReader(this).read(inStream);
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : " + e);
- }
- mData = d;
- }
-
- /**
- * Reads the exif tags from a file, clearing this ExifInterface object's
- * existing exif tags.
- *
- * @param inFileName a string representing the filepath to jpeg file.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void readExif(String inFileName) throws FileNotFoundException, IOException {
- if (inFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- InputStream is = null;
- try {
- is = new BufferedInputStream(new FileInputStream(inFileName));
- readExif(is);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- }
- is.close();
- }
-
- /**
- * Sets the exif tags, clearing this ExifInterface object's existing exif
- * tags.
- *
- * @param tags a collection of exif tags to set.
- */
- public void setExif(Collection<ExifTag> tags) {
- clearExif();
- setTags(tags);
- }
-
- /**
- * Clears this ExifInterface object's existing exif tags.
- */
- public void clearExif() {
- mData = new ExifData(DEFAULT_BYTE_ORDER);
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg image,
- * removing prior exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @param exifOutStream an OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(byte[] jpeg, OutputStream exifOutStream) throws IOException {
- if (jpeg == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- s.write(jpeg, 0, jpeg.length);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg compressed
- * bitmap, removing prior exif tags.
- *
- * @param bmap a bitmap to compress and write exif into.
- * @param exifOutStream the OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(Bitmap bmap, OutputStream exifOutStream) throws IOException {
- if (bmap == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg stream,
- * removing prior exif tags.
- *
- * @param jpegStream an InputStream containing a jpeg compressed image.
- * @param exifOutStream an OutputStream to which the jpeg image with added
- * exif tags will be written.
- * @throws IOException
- */
- public void writeExif(InputStream jpegStream, OutputStream exifOutStream) throws IOException {
- if (jpegStream == null || exifOutStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = getExifWriterStream(exifOutStream);
- doExifStreamIO(jpegStream, s);
- s.flush();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg image,
- * removing prior exif tags.
- *
- * @param jpeg a byte array containing a jpeg compressed image.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(byte[] jpeg, String exifOutFileName) throws FileNotFoundException,
- IOException {
- if (jpeg == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- s.write(jpeg, 0, jpeg.length);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg compressed
- * bitmap, removing prior exif tags.
- *
- * @param bmap a bitmap to compress and write exif into.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(Bitmap bmap, String exifOutFileName) throws FileNotFoundException,
- IOException {
- if (bmap == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg stream,
- * removing prior exif tags.
- *
- * @param jpegStream an InputStream containing a jpeg compressed image.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(InputStream jpegStream, String exifOutFileName)
- throws FileNotFoundException, IOException {
- if (jpegStream == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream s = null;
- try {
- s = getExifWriterStream(exifOutFileName);
- doExifStreamIO(jpegStream, s);
- s.flush();
- } catch (IOException e) {
- closeSilently(s);
- throw e;
- }
- s.close();
- }
-
- /**
- * Writes the tags from this ExifInterface object into a jpeg file, removing
- * prior exif tags.
- *
- * @param jpegFileName a String containing the filepath for a jpeg file.
- * @param exifOutFileName a String containing the filepath to which the jpeg
- * image with added exif tags will be written.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void writeExif(String jpegFileName, String exifOutFileName)
- throws FileNotFoundException, IOException {
- if (jpegFileName == null || exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- InputStream is = null;
- try {
- is = new FileInputStream(jpegFileName);
- writeExif(is, exifOutFileName);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- }
- is.close();
- }
-
- /**
- * Wraps an OutputStream object with an ExifOutputStream. Exif tags in this
- * ExifInterface object will be added to a jpeg image written to this
- * stream, removing prior exif tags. Other methods of this ExifInterface
- * object should not be called until the returned OutputStream has been
- * closed.
- *
- * @param outStream an OutputStream to wrap.
- * @return an OutputStream that wraps the outStream parameter, and adds exif
- * metadata. A jpeg image should be written to this stream.
- */
- public OutputStream getExifWriterStream(OutputStream outStream) {
- if (outStream == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- ExifOutputStream eos = new ExifOutputStream(outStream, this);
- eos.setExifData(mData);
- return eos;
- }
-
- /**
- * Returns an OutputStream object that writes to a file. Exif tags in this
- * ExifInterface object will be added to a jpeg image written to this
- * stream, removing prior exif tags. Other methods of this ExifInterface
- * object should not be called until the returned OutputStream has been
- * closed.
- *
- * @param exifOutFileName an String containing a filepath for a jpeg file.
- * @return an OutputStream that writes to the exifOutFileName file, and adds
- * exif metadata. A jpeg image should be written to this stream.
- * @throws FileNotFoundException
- */
- public OutputStream getExifWriterStream(String exifOutFileName) throws FileNotFoundException {
- if (exifOutFileName == null) {
- throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
- }
- OutputStream out = null;
- try {
- out = new FileOutputStream(exifOutFileName);
- } catch (FileNotFoundException e) {
- closeSilently(out);
- throw e;
- }
- return getExifWriterStream(out);
- }
-
- /**
- * Attempts to do an in-place rewrite the exif metadata in a file for the
- * given tags. If tags do not exist or do not have the same size as the
- * existing exif tags, this method will fail.
- *
- * @param filename a String containing a filepath for a jpeg file with exif
- * tags to rewrite.
- * @param tags tags that will be written into the jpeg file over existing
- * tags if possible.
- * @return true if success, false if could not overwrite. If false, no
- * changes are made to the file.
- * @throws FileNotFoundException
- * @throws IOException
- */
- public boolean rewriteExif(String filename, Collection<ExifTag> tags)
- throws FileNotFoundException, IOException {
- RandomAccessFile file = null;
- InputStream is = null;
- boolean ret;
- try {
- File temp = new File(filename);
- is = new BufferedInputStream(new FileInputStream(temp));
-
- // Parse beginning of APP1 in exif to find size of exif header.
- ExifParser parser = null;
- try {
- parser = ExifParser.parse(is, this);
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : ", e);
- }
- long exifSize = parser.getOffsetToExifEndFromSOF();
-
- // Free up resources
- is.close();
- is = null;
-
- // Open file for memory mapping.
- file = new RandomAccessFile(temp, "rw");
- long fileLength = file.length();
- if (fileLength < exifSize) {
- throw new IOException("Filesize changed during operation");
- }
-
- // Map only exif header into memory.
- ByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, exifSize);
-
- // Attempt to overwrite tag values without changing lengths (avoids
- // file copy).
- ret = rewriteExif(buf, tags);
- } catch (IOException e) {
- closeSilently(file);
- throw e;
- } finally {
- closeSilently(is);
- }
- file.close();
- return ret;
- }
-
- /**
- * Attempts to do an in-place rewrite the exif metadata in a ByteBuffer for
- * the given tags. If tags do not exist or do not have the same size as the
- * existing exif tags, this method will fail.
- *
- * @param buf a ByteBuffer containing a jpeg file with existing exif tags to
- * rewrite.
- * @param tags tags that will be written into the jpeg ByteBuffer over
- * existing tags if possible.
- * @return true if success, false if could not overwrite. If false, no
- * changes are made to the ByteBuffer.
- * @throws IOException
- */
- public boolean rewriteExif(ByteBuffer buf, Collection<ExifTag> tags) throws IOException {
- ExifModifier mod = null;
- try {
- mod = new ExifModifier(buf, this);
- for (ExifTag t : tags) {
- mod.modifyTag(t);
- }
- return mod.commit();
- } catch (ExifInvalidFormatException e) {
- throw new IOException("Invalid exif format : " + e);
- }
- }
-
- /**
- * Attempts to do an in-place rewrite of the exif metadata. If this fails,
- * fall back to overwriting file. This preserves tags that are not being
- * rewritten.
- *
- * @param filename a String containing a filepath for a jpeg file.
- * @param tags tags that will be written into the jpeg file over existing
- * tags if possible.
- * @throws FileNotFoundException
- * @throws IOException
- * @see #rewriteExif
- */
- public void forceRewriteExif(String filename, Collection<ExifTag> tags)
- throws FileNotFoundException,
- IOException {
- // Attempt in-place write
- if (!rewriteExif(filename, tags)) {
- // Fall back to doing a copy
- ExifData tempData = mData;
- mData = new ExifData(DEFAULT_BYTE_ORDER);
- FileInputStream is = null;
- ByteArrayOutputStream bytes = null;
- try {
- is = new FileInputStream(filename);
- bytes = new ByteArrayOutputStream();
- doExifStreamIO(is, bytes);
- byte[] imageBytes = bytes.toByteArray();
- readExif(imageBytes);
- setTags(tags);
- writeExif(imageBytes, filename);
- } catch (IOException e) {
- closeSilently(is);
- throw e;
- } finally {
- is.close();
- // Prevent clobbering of mData
- mData = tempData;
- }
- }
- }
-
- /**
- * Attempts to do an in-place rewrite of the exif metadata using the tags in
- * this ExifInterface object. If this fails, fall back to overwriting file.
- * This preserves tags that are not being rewritten.
- *
- * @param filename a String containing a filepath for a jpeg file.
- * @throws FileNotFoundException
- * @throws IOException
- * @see #rewriteExif
- */
- public void forceRewriteExif(String filename) throws FileNotFoundException, IOException {
- forceRewriteExif(filename, getAllTags());
- }
-
- /**
- * Get the exif tags in this ExifInterface object or null if none exist.
- *
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getAllTags() {
- return mData.getAllTags();
- }
-
- /**
- * Returns a list of ExifTags that share a TID (which can be obtained by
- * calling {@link #getTrueTagKey} on a defined tag constant) or null if none
- * exist.
- *
- * @param tagId a TID as defined in the exif standard (or with
- * {@link #defineTag}).
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getTagsForTagId(short tagId) {
- return mData.getAllTagsForTagId(tagId);
- }
-
- /**
- * Returns a list of ExifTags that share an IFD (which can be obtained by
- * calling {@link #getTrueIFD} on a defined tag constant) or null if none
- * exist.
- *
- * @param ifdId an IFD as defined in the exif standard (or with
- * {@link #defineTag}).
- * @return a List of {@link ExifTag}s.
- */
- public List<ExifTag> getTagsForIfdId(int ifdId) {
- return mData.getAllTagsForIfd(ifdId);
- }
-
- /**
- * Gets an ExifTag for an IFD other than the tag's default.
- *
- * @see #getTag
- */
- public ExifTag getTag(int tagId, int ifdId) {
- if (!ExifTag.isValidIfd(ifdId)) {
- return null;
- }
- return mData.getTag(getTrueTagKey(tagId), ifdId);
- }
-
- /**
- * Returns the ExifTag in that tag's default IFD for a defined tag constant
- * or null if none exists.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return an {@link ExifTag} or null if none exists.
- */
- public ExifTag getTag(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTag(tagId, ifdId);
- }
-
- /**
- * Gets a tag value for an IFD other than the tag's default.
- *
- * @see #getTagValue
- */
- public Object getTagValue(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- return (t == null) ? null : t.getValue();
- }
-
- /**
- * Returns the value of the ExifTag in that tag's default IFD for a defined
- * tag constant or null if none exists or the value could not be cast into
- * the return type.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the value of the ExifTag or null if none exists.
- */
- public Object getTagValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagValue(tagId, ifdId);
- }
-
- /*
- * Getter methods that are similar to getTagValue. Null is returned if the
- * tag value cannot be cast into the return type.
- */
-
- /**
- * @see #getTagValue
- */
- public String getTagStringValue(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsString();
- }
-
- /**
- * @see #getTagValue
- */
- public String getTagStringValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagStringValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Long getTagLongValue(int tagId, int ifdId) {
- long[] l = getTagLongValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return new Long(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Long getTagLongValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagLongValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Integer getTagIntValue(int tagId, int ifdId) {
- int[] l = getTagIntValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return new Integer(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Integer getTagIntValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagIntValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Byte getTagByteValue(int tagId, int ifdId) {
- byte[] l = getTagByteValues(tagId, ifdId);
- if (l == null || l.length <= 0) {
- return null;
- }
- return new Byte(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Byte getTagByteValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagByteValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational getTagRationalValue(int tagId, int ifdId) {
- Rational[] l = getTagRationalValues(tagId, ifdId);
- if (l == null || l.length == 0) {
- return null;
- }
- return new Rational(l[0]);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational getTagRationalValue(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagRationalValue(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public long[] getTagLongValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsLongs();
- }
-
- /**
- * @see #getTagValue
- */
- public long[] getTagLongValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagLongValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public int[] getTagIntValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsInts();
- }
-
- /**
- * @see #getTagValue
- */
- public int[] getTagIntValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagIntValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public byte[] getTagByteValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsBytes();
- }
-
- /**
- * @see #getTagValue
- */
- public byte[] getTagByteValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagByteValues(tagId, ifdId);
- }
-
- /**
- * @see #getTagValue
- */
- public Rational[] getTagRationalValues(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return null;
- }
- return t.getValueAsRationals();
- }
-
- /**
- * @see #getTagValue
- */
- public Rational[] getTagRationalValues(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return getTagRationalValues(tagId, ifdId);
- }
-
- /**
- * Checks whether a tag has a defined number of elements.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return true if the tag has a defined number of elements.
- */
- public boolean isTagCountDefined(int tagId) {
- int info = getTagInfo().get(tagId);
- // No value in info can be zero, as all tags have a non-zero type
- if (info == 0) {
- return false;
- }
- return getComponentCountFromInfo(info) != ExifTag.SIZE_UNDEFINED;
- }
-
- /**
- * Gets the defined number of elements for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the number of elements or {@link ExifTag#SIZE_UNDEFINED} if the
- * tag or the number of elements is not defined.
- */
- public int getDefinedTagCount(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return ExifTag.SIZE_UNDEFINED;
- }
- return getComponentCountFromInfo(info);
- }
-
- /**
- * Gets the number of elements for an ExifTag in a given IFD.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD containing the ExifTag to check.
- * @return the number of elements in the ExifTag, if the tag's size is
- * undefined this will return the actual number of elements that is
- * in the ExifTag's value.
- */
- public int getActualTagCount(int tagId, int ifdId) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return 0;
- }
- return t.getComponentCount();
- }
-
- /**
- * Gets the default IFD for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the default IFD for a tag definition or {@link #IFD_NULL} if no
- * definition exists.
- */
- public int getDefinedTagDefaultIfd(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == DEFINITION_NULL) {
- return IFD_NULL;
- }
- return getTrueIfd(tagId);
- }
-
- /**
- * Gets the defined type for a tag.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @return the type.
- * @see ExifTag#getDataType()
- */
- public short getDefinedTagType(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return -1;
- }
- return getTypeFromInfo(info);
- }
-
- /**
- * Returns true if tag TID is one of the following: {@link TAG_EXIF_IFD},
- * {@link TAG_GPS_IFD}, {@link TAG_JPEG_INTERCHANGE_FORMAT},
- * {@link TAG_STRIP_OFFSETS}, {@link TAG_INTEROPERABILITY_IFD}
- * <p>
- * Note: defining tags with these TID's is disallowed.
- *
- * @param tag a tag's TID (can be obtained from a defined tag constant with
- * {@link #getTrueTagKey}).
- * @return true if the TID is that of an offset tag.
- */
- protected static boolean isOffsetTag(short tag) {
- return sOffsetTags.contains(tag);
- }
-
- /**
- * Creates a tag for a defined tag constant in a given IFD if that IFD is
- * allowed for the tag. This method will fail anytime the appropriate
- * {@link ExifTag#setValue} for this tag's datatype would fail.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD that the tag should be in.
- * @param val the value of the tag to set.
- * @return an ExifTag object or null if one could not be constructed.
- * @see #buildTag
- */
- public ExifTag buildTag(int tagId, int ifdId, Object val) {
- int info = getTagInfo().get(tagId);
- if (info == 0 || val == null) {
- return null;
- }
- short type = getTypeFromInfo(info);
- int definedCount = getComponentCountFromInfo(info);
- boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
- if (!ExifInterface.isIfdAllowed(info, ifdId)) {
- return null;
- }
- ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
- if (!t.setValue(val)) {
- return null;
- }
- return t;
- }
-
- /**
- * Creates a tag for a defined tag constant in the tag's default IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param val the tag's value.
- * @return an ExifTag object.
- */
- public ExifTag buildTag(int tagId, Object val) {
- int ifdId = getTrueIfd(tagId);
- return buildTag(tagId, ifdId, val);
- }
-
- protected ExifTag buildUninitializedTag(int tagId) {
- int info = getTagInfo().get(tagId);
- if (info == 0) {
- return null;
- }
- short type = getTypeFromInfo(info);
- int definedCount = getComponentCountFromInfo(info);
- boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
- int ifdId = getTrueIfd(tagId);
- ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
- return t;
- }
-
- /**
- * Sets the value of an ExifTag if it exists in the given IFD. The value
- * must be the correct type and length for that ExifTag.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD that the ExifTag is in.
- * @param val the value to set.
- * @return true if success, false if the ExifTag doesn't exist or the value
- * is the wrong type/length.
- * @see #setTagValue
- */
- public boolean setTagValue(int tagId, int ifdId, Object val) {
- ExifTag t = getTag(tagId, ifdId);
- if (t == null) {
- return false;
- }
- return t.setValue(val);
- }
-
- /**
- * Sets the value of an ExifTag if it exists it's default IFD. The value
- * must be the correct type and length for that ExifTag.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param val the value to set.
- * @return true if success, false if the ExifTag doesn't exist or the value
- * is the wrong type/length.
- */
- public boolean setTagValue(int tagId, Object val) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- return setTagValue(tagId, ifdId, val);
- }
-
- /**
- * Puts an ExifTag into this ExifInterface object's tags, removing a
- * previous ExifTag with the same TID and IFD. The IFD it is put into will
- * be the one the tag was created with in {@link #buildTag}.
- *
- * @param tag an ExifTag to put into this ExifInterface's tags.
- * @return the previous ExifTag with the same TID and IFD or null if none
- * exists.
- */
- public ExifTag setTag(ExifTag tag) {
- return mData.addTag(tag);
- }
-
- /**
- * Puts a collection of ExifTags into this ExifInterface objects's tags. Any
- * previous ExifTags with the same TID and IFDs will be removed.
- *
- * @param tags a Collection of ExifTags.
- * @see #setTag
- */
- public void setTags(Collection<ExifTag> tags) {
- for (ExifTag t : tags) {
- setTag(t);
- }
- }
-
- /**
- * Removes the ExifTag for a tag constant from the given IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- * @param ifdId the IFD of the ExifTag to remove.
- */
- public void deleteTag(int tagId, int ifdId) {
- mData.removeTag(getTrueTagKey(tagId), ifdId);
- }
-
- /**
- * Removes the ExifTag for a tag constant from that tag's default IFD.
- *
- * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- */
- public void deleteTag(int tagId) {
- int ifdId = getDefinedTagDefaultIfd(tagId);
- deleteTag(tagId, ifdId);
- }
-
- /**
- * Creates a new tag definition in this ExifInterface object for a given TID
- * and default IFD. Creating a definition with the same TID and default IFD
- * as a previous definition will override it.
- *
- * @param tagId the TID for the tag.
- * @param defaultIfd the default IFD for the tag.
- * @param tagType the type of the tag (see {@link ExifTag#getDataType()}).
- * @param defaultComponentCount the number of elements of this tag's type in
- * the tags value.
- * @param allowedIfds the IFD's this tag is allowed to be put in.
- * @return the defined tag constant (e.g. {@link #TAG_IMAGE_WIDTH}) or
- * {@link #TAG_NULL} if the definition could not be made.
- */
- public int setTagDefinition(short tagId, int defaultIfd, short tagType,
- short defaultComponentCount, int[] allowedIfds) {
- if (sBannedDefines.contains(tagId)) {
- return TAG_NULL;
- }
- if (ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) {
- int tagDef = defineTag(defaultIfd, tagId);
- if (tagDef == TAG_NULL) {
- return TAG_NULL;
- }
- int[] otherDefs = getTagDefinitionsForTagId(tagId);
- SparseIntArray infos = getTagInfo();
- // Make sure defaultIfd is in allowedIfds
- boolean defaultCheck = false;
- for (int i : allowedIfds) {
- if (defaultIfd == i) {
- defaultCheck = true;
- }
- if (!ExifTag.isValidIfd(i)) {
- return TAG_NULL;
- }
- }
- if (!defaultCheck) {
- return TAG_NULL;
- }
-
- int ifdFlags = getFlagsFromAllowedIfds(allowedIfds);
- // Make sure no identical tags can exist in allowedIfds
- if (otherDefs != null) {
- for (int def : otherDefs) {
- int tagInfo = infos.get(def);
- int allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo);
- if ((ifdFlags & allowedFlags) != 0) {
- return TAG_NULL;
- }
- }
- }
- getTagInfo().put(tagDef, ifdFlags << 24 | (tagType << 16) | defaultComponentCount);
- return tagDef;
- }
- return TAG_NULL;
- }
-
- protected int getTagDefinition(short tagId, int defaultIfd) {
- return getTagInfo().get(defineTag(defaultIfd, tagId));
- }
-
- protected int[] getTagDefinitionsForTagId(short tagId) {
- int[] ifds = IfdData.getIfds();
- int[] defs = new int[ifds.length];
- int counter = 0;
- SparseIntArray infos = getTagInfo();
- for (int i : ifds) {
- int def = defineTag(i, tagId);
- if (infos.get(def) != DEFINITION_NULL) {
- defs[counter++] = def;
- }
- }
- if (counter == 0) {
- return null;
- }
-
- return Arrays.copyOfRange(defs, 0, counter);
- }
-
- protected int getTagDefinitionForTag(ExifTag tag) {
- short type = tag.getDataType();
- int count = tag.getComponentCount();
- int ifd = tag.getIfd();
- return getTagDefinitionForTag(tag.getTagId(), type, count, ifd);
- }
-
- protected int getTagDefinitionForTag(short tagId, short type, int count, int ifd) {
- int[] defs = getTagDefinitionsForTagId(tagId);
- if (defs == null) {
- return TAG_NULL;
- }
- SparseIntArray infos = getTagInfo();
- int ret = TAG_NULL;
- for (int i : defs) {
- int info = infos.get(i);
- short def_type = getTypeFromInfo(info);
- int def_count = getComponentCountFromInfo(info);
- int[] def_ifds = getAllowedIfdsFromInfo(info);
- boolean valid_ifd = false;
- for (int j : def_ifds) {
- if (j == ifd) {
- valid_ifd = true;
- break;
- }
- }
- if (valid_ifd && type == def_type
- && (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)) {
- ret = i;
- break;
- }
- }
- return ret;
- }
-
- /**
- * Removes a tag definition for given defined tag constant.
- *
- * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
- */
- public void removeTagDefinition(int tagId) {
- getTagInfo().delete(tagId);
- }
-
- /**
- * Resets tag definitions to the default ones.
- */
- public void resetTagDefinitions() {
- mTagInfo = null;
- }
-
- /**
- * Returns the thumbnail from IFD1 as a bitmap, or null if none exists.
- *
- * @return the thumbnail as a bitmap.
- */
- public Bitmap getThumbnailBitmap() {
- if (mData.hasCompressedThumbnail()) {
- byte[] thumb = mData.getCompressedThumbnail();
- return BitmapFactory.decodeByteArray(thumb, 0, thumb.length);
- } else if (mData.hasUncompressedStrip()) {
- // TODO: implement uncompressed
- }
- return null;
- }
-
- /**
- * Returns the thumbnail from IFD1 as a byte array, or null if none exists.
- * The bytes may either be an uncompressed strip as specified in the exif
- * standard or a jpeg compressed image.
- *
- * @return the thumbnail as a byte array.
- */
- public byte[] getThumbnailBytes() {
- if (mData.hasCompressedThumbnail()) {
- return mData.getCompressedThumbnail();
- } else if (mData.hasUncompressedStrip()) {
- // TODO: implement this
- }
- return null;
- }
-
- /**
- * Returns the thumbnail if it is jpeg compressed, or null if none exists.
- *
- * @return the thumbnail as a byte array.
- */
- public byte[] getThumbnail() {
- return mData.getCompressedThumbnail();
- }
-
- /**
- * Check if thumbnail is compressed.
- *
- * @return true if the thumbnail is compressed.
- */
- public boolean isThumbnailCompressed() {
- return mData.hasCompressedThumbnail();
- }
-
- /**
- * Check if thumbnail exists.
- *
- * @return true if a compressed thumbnail exists.
- */
- public boolean hasThumbnail() {
- // TODO: add back in uncompressed strip
- return mData.hasCompressedThumbnail();
- }
-
- // TODO: uncompressed thumbnail setters
-
- /**
- * Sets the thumbnail to be a jpeg compressed image. Clears any prior
- * thumbnail.
- *
- * @param thumb a byte array containing a jpeg compressed image.
- * @return true if the thumbnail was set.
- */
- public boolean setCompressedThumbnail(byte[] thumb) {
- mData.clearThumbnailAndStrips();
- mData.setCompressedThumbnail(thumb);
- return true;
- }
-
- /**
- * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior
- * thumbnail.
- *
- * @param thumb a bitmap to compress to a jpeg thumbnail.
- * @return true if the thumbnail was set.
- */
- public boolean setCompressedThumbnail(Bitmap thumb) {
- ByteArrayOutputStream thumbnail = new ByteArrayOutputStream();
- if (!thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) {
- return false;
- }
- return setCompressedThumbnail(thumbnail.toByteArray());
- }
-
- /**
- * Clears the compressed thumbnail if it exists.
- */
- public void removeCompressedThumbnail() {
- mData.setCompressedThumbnail(null);
- }
-
- // Convenience methods:
-
- /**
- * Decodes the user comment tag into string as specified in the EXIF
- * standard. Returns null if decoding failed.
- */
- public String getUserComment() {
- return mData.getUserComment();
- }
-
- /**
- * Returns the Orientation ExifTag value for a given number of degrees.
- *
- * @param degrees the amount an image is rotated in degrees.
- */
- public static short getOrientationValueForRotation(int degrees) {
- degrees %= 360;
- if (degrees < 0) {
- degrees += 360;
- }
- if (degrees < 90) {
- return Orientation.TOP_LEFT; // 0 degrees
- } else if (degrees < 180) {
- return Orientation.RIGHT_TOP; // 90 degrees cw
- } else if (degrees < 270) {
- return Orientation.BOTTOM_LEFT; // 180 degrees
- } else {
- return Orientation.RIGHT_BOTTOM; // 270 degrees cw
- }
- }
-
- /**
- * Returns the rotation degrees corresponding to an ExifTag Orientation
- * value.
- *
- * @param orientation the ExifTag Orientation value.
- */
- public static int getRotationForOrientationValue(short orientation) {
- switch (orientation) {
- case Orientation.TOP_LEFT:
- return 0;
- case Orientation.RIGHT_TOP:
- return 90;
- case Orientation.BOTTOM_LEFT:
- return 180;
- case Orientation.RIGHT_BOTTOM:
- return 270;
- default:
- return 0;
- }
- }
-
- /**
- * Gets the double representation of the GPS latitude or longitude
- * coordinate.
- *
- * @param coordinate an array of 3 Rationals representing the degrees,
- * minutes, and seconds of the GPS location as defined in the
- * exif specification.
- * @param reference a GPS reference reperesented by a String containing "N",
- * "S", "E", or "W".
- * @return the GPS coordinate represented as degrees + minutes/60 +
- * seconds/3600
- */
- public static double convertLatOrLongToDouble(Rational[] coordinate, String reference) {
- try {
- double degrees = coordinate[0].toDouble();
- double minutes = coordinate[1].toDouble();
- double seconds = coordinate[2].toDouble();
- double result = degrees + minutes / 60.0 + seconds / 3600.0;
- if ((reference.equals("S") || reference.equals("W"))) {
- return -result;
- }
- return result;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * Gets the GPS latitude and longitude as a pair of doubles from this
- * ExifInterface object's tags, or null if the necessary tags do not exist.
- *
- * @return an array of 2 doubles containing the latitude, and longitude
- * respectively.
- * @see #convertLatOrLongToDouble
- */
- public double[] getLatLongAsDoubles() {
- Rational[] latitude = getTagRationalValues(TAG_GPS_LATITUDE);
- String latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF);
- Rational[] longitude = getTagRationalValues(TAG_GPS_LONGITUDE);
- String longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF);
- if (latitude == null || longitude == null || latitudeRef == null || longitudeRef == null
- || latitude.length < 3 || longitude.length < 3) {
- return null;
- }
- double[] latLon = new double[2];
- latLon[0] = convertLatOrLongToDouble(latitude, latitudeRef);
- latLon[1] = convertLatOrLongToDouble(longitude, longitudeRef);
- return latLon;
- }
-
- private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
- private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss";
- private final DateFormat mDateTimeStampFormat = new SimpleDateFormat(DATETIME_FORMAT_STR);
- private final DateFormat mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
- private final Calendar mGPSTimeStampCalendar = Calendar
- .getInstance(TimeZone.getTimeZone("UTC"));
-
- /**
- * Creates, formats, and sets the DateTimeStamp tag for one of:
- * {@link #TAG_DATE_TIME}, {@link #TAG_DATE_TIME_DIGITIZED},
- * {@link #TAG_DATE_TIME_ORIGINAL}.
- *
- * @param tagId one of the DateTimeStamp tags.
- * @param timestamp a timestamp to format.
- * @param timezone a TimeZone object.
- * @return true if success, false if the tag could not be set.
- */
- public boolean addDateTimeStampTag(int tagId, long timestamp, TimeZone timezone) {
- if (tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED
- || tagId == TAG_DATE_TIME_ORIGINAL) {
- mDateTimeStampFormat.setTimeZone(timezone);
- ExifTag t = buildTag(tagId, mDateTimeStampFormat.format(timestamp));
- if (t == null) {
- return false;
- }
- setTag(t);
- } else {
- return false;
- }
- return true;
- }
-
- /**
- * Creates and sets all to the GPS tags for a give latitude and longitude.
- *
- * @param latitude a GPS latitude coordinate.
- * @param longitude a GPS longitude coordinate.
- * @return true if success, false if they could not be created or set.
- */
- public boolean addGpsTags(double latitude, double longitude) {
- ExifTag latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude));
- ExifTag longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude));
- ExifTag latRefTag = buildTag(TAG_GPS_LATITUDE_REF,
- latitude >= 0 ? ExifInterface.GpsLatitudeRef.NORTH
- : ExifInterface.GpsLatitudeRef.SOUTH);
- ExifTag longRefTag = buildTag(TAG_GPS_LONGITUDE_REF,
- longitude >= 0 ? ExifInterface.GpsLongitudeRef.EAST
- : ExifInterface.GpsLongitudeRef.WEST);
- if (latTag == null || longTag == null || latRefTag == null || longRefTag == null) {
- return false;
- }
- setTag(latTag);
- setTag(longTag);
- setTag(latRefTag);
- setTag(longRefTag);
- return true;
- }
-
- /**
- * Creates and sets the GPS timestamp tag.
- *
- * @param timestamp a GPS timestamp.
- * @return true if success, false if could not be created or set.
- */
- public boolean addGpsDateTimeStampTag(long timestamp) {
- ExifTag t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp));
- if (t == null) {
- return false;
- }
- setTag(t);
- mGPSTimeStampCalendar.setTimeInMillis(timestamp);
- t = buildTag(TAG_GPS_TIME_STAMP, new Rational[] {
- new Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY), 1),
- new Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE), 1),
- new Rational(mGPSTimeStampCalendar.get(Calendar.SECOND), 1)
- });
- if (t == null) {
- return false;
- }
- setTag(t);
- return true;
- }
-
- private static Rational[] toExifLatLong(double value) {
- // convert to the format dd/1 mm/1 ssss/100
- value = Math.abs(value);
- int degrees = (int) value;
- value = (value - degrees) * 60;
- int minutes = (int) value;
- value = (value - minutes) * 6000;
- int seconds = (int) value;
- return new Rational[] {
- new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100)
- };
- }
-
- private void doExifStreamIO(InputStream is, OutputStream os) throws IOException {
- byte[] buf = new byte[1024];
- int ret = is.read(buf, 0, 1024);
- while (ret != -1) {
- os.write(buf, 0, ret);
- ret = is.read(buf, 0, 1024);
- }
- }
-
- protected static void closeSilently(Closeable c) {
- if (c != null) {
- try {
- c.close();
- } catch (Throwable e) {
- // ignored
- }
- }
- }
-
- private SparseIntArray mTagInfo = null;
-
- protected SparseIntArray getTagInfo() {
- if (mTagInfo == null) {
- mTagInfo = new SparseIntArray();
- initTagInfo();
- }
- return mTagInfo;
- }
-
- private void initTagInfo() {
- /**
- * We put tag information in a 4-bytes integer. The first byte a bitmask
- * representing the allowed IFDs of the tag, the second byte is the data
- * type, and the last two byte are a short value indicating the default
- * component count of this tag.
- */
- // IFD0 tags
- int[] ifdAllowedIfds = {
- IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1
- };
- int ifdFlags = getFlagsFromAllowedIfds(ifdAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_MAKE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_IMAGE_WIDTH,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_IMAGE_LENGTH,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_BITS_PER_SAMPLE,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_COMPRESSION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16
- | 1);
- mTagInfo.put(ExifInterface.TAG_SAMPLES_PER_PIXEL,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PLANAR_CONFIGURATION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_POSITIONING,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_X_RESOLUTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_Y_RESOLUTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_RESOLUTION_UNIT,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_STRIP_OFFSETS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ROWS_PER_STRIP,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_STRIP_BYTE_COUNTS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_TRANSFER_FUNCTION,
- ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 * 256);
- mTagInfo.put(ExifInterface.TAG_WHITE_POINT,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_PRIMARY_CHROMATICITIES,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
- mTagInfo.put(ExifInterface.TAG_Y_CB_CR_COEFFICIENTS,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_REFERENCE_BLACK_WHITE,
- ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_IMAGE_DESCRIPTION,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_MAKE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_MODEL,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SOFTWARE,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ARTIST,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_COPYRIGHT,
- ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_EXIF_IFD,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_IFD,
- ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // IFD1 tags
- int[] ifd1AllowedIfds = {
- IfdId.TYPE_IFD_1
- };
- int ifdFlags1 = getFlagsFromAllowedIfds(ifd1AllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT,
- ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
- ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // Exif tags
- int[] exifAllowedIfds = {
- IfdId.TYPE_IFD_EXIF
- };
- int exifFlags = getFlagsFromAllowedIfds(exifAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_EXIF_VERSION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_FLASHPIX_VERSION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_COLOR_SPACE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_COMPONENTS_CONFIGURATION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PIXEL_X_DIMENSION,
- exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_PIXEL_Y_DIMENSION,
- exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_MAKER_NOTE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_USER_COMMENT,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_RELATED_SOUND_FILE,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 13);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME_ORIGINAL,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_DATE_TIME_DIGITIZED,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_ORIGINAL,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_DIGITIZED,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_IMAGE_UNIQUE_ID,
- exifFlags | ExifTag.TYPE_ASCII << 16 | 33);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_TIME,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_F_NUMBER,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_PROGRAM,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SPECTRAL_SENSITIVITY,
- exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_ISO_SPEED_RATINGS,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_OECF,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_APERTURE_VALUE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_BRIGHTNESS_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_BIAS_VALUE,
- exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_MAX_APERTURE_VALUE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_METERING_MODE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_LIGHT_SOURCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FLASH,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_AREA,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_FLASH_ENERGY,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_LOCATION,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_INDEX,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SENSING_METHOD,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FILE_SOURCE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SCENE_TYPE,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_CFA_PATTERN,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_CUSTOM_RENDERED,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_EXPOSURE_MODE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_WHITE_BALANCE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_DIGITAL_ZOOM_RATIO,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH_IN_35_MM_FILE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SCENE_CAPTURE_TYPE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GAIN_CONTROL,
- exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_CONTRAST,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SATURATION,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_SHARPNESS,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION,
- exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE_RANGE,
- exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags
- | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
- // GPS tag
- int[] gpsAllowedIfds = {
- IfdId.TYPE_IFD_GPS
- };
- int gpsFlags = getFlagsFromAllowedIfds(gpsAllowedIfds) << 24;
- mTagInfo.put(ExifInterface.TAG_GPS_VERSION_ID,
- gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 4);
- mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE,
- gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE,
- gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE_REF,
- gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_TIME_STAMP,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
- mTagInfo.put(ExifInterface.TAG_GPS_SATTELLITES,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_STATUS,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_MEASURE_MODE,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DOP,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_SPEED_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_SPEED,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_TRACK_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_TRACK,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_MAP_DATUM,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE_REF,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
- mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE,
- gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
- mTagInfo.put(ExifInterface.TAG_GPS_PROCESSING_METHOD,
- gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_AREA_INFORMATION,
- gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
- mTagInfo.put(ExifInterface.TAG_GPS_DATE_STAMP,
- gpsFlags | ExifTag.TYPE_ASCII << 16 | 11);
- mTagInfo.put(ExifInterface.TAG_GPS_DIFFERENTIAL,
- gpsFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 11);
- // Interoperability tag
- int[] interopAllowedIfds = {
- IfdId.TYPE_IFD_INTEROPERABILITY
- };
- int interopFlags = getFlagsFromAllowedIfds(interopAllowedIfds) << 24;
- mTagInfo.put(TAG_INTEROPERABILITY_INDEX, interopFlags | ExifTag.TYPE_ASCII << 16
- | ExifTag.SIZE_UNDEFINED);
- }
-
- protected static int getAllowedIfdFlagsFromInfo(int info) {
- return info >>> 24;
- }
-
- protected static int[] getAllowedIfdsFromInfo(int info) {
- int ifdFlags = getAllowedIfdFlagsFromInfo(info);
- int[] ifds = IfdData.getIfds();
- ArrayList<Integer> l = new ArrayList<Integer>();
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- int flag = (ifdFlags >> i) & 1;
- if (flag == 1) {
- l.add(ifds[i]);
- }
- }
- if (l.size() <= 0) {
- return null;
- }
- int[] ret = new int[l.size()];
- int j = 0;
- for (int i : l) {
- ret[j++] = i;
- }
- return ret;
- }
-
- protected static boolean isIfdAllowed(int info, int ifd) {
- int[] ifds = IfdData.getIfds();
- int ifdFlags = getAllowedIfdFlagsFromInfo(info);
- for (int i = 0; i < ifds.length; i++) {
- if (ifd == ifds[i] && ((ifdFlags >> i) & 1) == 1) {
- return true;
- }
- }
- return false;
- }
-
- protected static int getFlagsFromAllowedIfds(int[] allowedIfds) {
- if (allowedIfds == null || allowedIfds.length == 0) {
- return 0;
- }
- int flags = 0;
- int[] ifds = IfdData.getIfds();
- for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
- for (int j : allowedIfds) {
- if (ifds[i] == j) {
- flags |= 1 << i;
- break;
- }
- }
- }
- return flags;
- }
-
- protected static short getTypeFromInfo(int info) {
- return (short) ((info >> 16) & 0x0ff);
- }
-
- protected static int getComponentCountFromInfo(int info) {
- return info & 0x0ffff;
- }
-
-}
diff --git a/src/com/android/camera/exif/ExifInvalidFormatException.java b/src/com/android/camera/exif/ExifInvalidFormatException.java
deleted file mode 100644
index 2777a5133..000000000
--- a/src/com/android/camera/exif/ExifInvalidFormatException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-public class ExifInvalidFormatException extends Exception {
- public ExifInvalidFormatException(String meg) {
- super(meg);
- }
-} \ No newline at end of file
diff --git a/src/com/android/camera/exif/ExifModifier.java b/src/com/android/camera/exif/ExifModifier.java
deleted file mode 100644
index bed038c4a..000000000
--- a/src/com/android/camera/exif/ExifModifier.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.List;
-
-class ExifModifier {
- public static final String TAG = "ExifModifier";
- public static final boolean DEBUG = false;
- private final ByteBuffer mByteBuffer;
- private final ExifData mTagToModified;
- private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
- private final ExifInterface mInterface;
- private int mOffsetBase;
-
- private static class TagOffset {
- final int mOffset;
- final ExifTag mTag;
-
- TagOffset(ExifTag tag, int offset) {
- mTag = tag;
- mOffset = offset;
- }
- }
-
- protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
- ExifInvalidFormatException {
- mByteBuffer = byteBuffer;
- mOffsetBase = byteBuffer.position();
- mInterface = iRef;
- InputStream is = null;
- try {
- is = new ByteBufferInputStream(byteBuffer);
- // Do not require any IFD;
- ExifParser parser = ExifParser.parse(is, mInterface);
- mTagToModified = new ExifData(parser.getByteOrder());
- mOffsetBase += parser.getTiffStartPosition();
- mByteBuffer.position(0);
- } finally {
- ExifInterface.closeSilently(is);
- }
- }
-
- protected ByteOrder getByteOrder() {
- return mTagToModified.getByteOrder();
- }
-
- protected boolean commit() throws IOException, ExifInvalidFormatException {
- InputStream is = null;
- try {
- is = new ByteBufferInputStream(mByteBuffer);
- int flag = 0;
- IfdData[] ifdDatas = new IfdData[] {
- mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
- mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
- };
-
- if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
- flag |= ExifParser.OPTION_IFD_0;
- }
- if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
- flag |= ExifParser.OPTION_IFD_1;
- }
- if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
- flag |= ExifParser.OPTION_IFD_EXIF;
- }
- if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
- flag |= ExifParser.OPTION_IFD_GPS;
- }
- if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
- flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
- }
-
- ExifParser parser = ExifParser.parse(is, flag, mInterface);
- int event = parser.next();
- IfdData currIfd = null;
- while (event != ExifParser.EVENT_END) {
- switch (event) {
- case ExifParser.EVENT_START_OF_IFD:
- currIfd = ifdDatas[parser.getCurrentIfd()];
- if (currIfd == null) {
- parser.skipRemainingTagsInCurrentIfd();
- }
- break;
- case ExifParser.EVENT_NEW_TAG:
- ExifTag oldTag = parser.getTag();
- ExifTag newTag = currIfd.getTag(oldTag.getTagId());
- if (newTag != null) {
- if (newTag.getComponentCount() != oldTag.getComponentCount()
- || newTag.getDataType() != oldTag.getDataType()) {
- return false;
- } else {
- mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
- currIfd.removeTag(oldTag.getTagId());
- if (currIfd.getTagCount() == 0) {
- parser.skipRemainingTagsInCurrentIfd();
- }
- }
- }
- break;
- }
- event = parser.next();
- }
- for (IfdData ifd : ifdDatas) {
- if (ifd != null && ifd.getTagCount() > 0) {
- return false;
- }
- }
- modify();
- } finally {
- ExifInterface.closeSilently(is);
- }
- return true;
- }
-
- private void modify() {
- mByteBuffer.order(getByteOrder());
- for (TagOffset tagOffset : mTagOffsets) {
- writeTagValue(tagOffset.mTag, tagOffset.mOffset);
- }
- }
-
- private void writeTagValue(ExifTag tag, int offset) {
- if (DEBUG) {
- Log.v(TAG, "modifying tag to: \n" + tag.toString());
- Log.v(TAG, "at offset: " + offset);
- }
- mByteBuffer.position(offset + mOffsetBase);
- switch (tag.getDataType()) {
- case ExifTag.TYPE_ASCII:
- byte buf[] = tag.getStringByte();
- if (buf.length == tag.getComponentCount()) {
- buf[buf.length - 1] = 0;
- mByteBuffer.put(buf);
- } else {
- mByteBuffer.put(buf);
- mByteBuffer.put((byte) 0);
- }
- break;
- case ExifTag.TYPE_LONG:
- case ExifTag.TYPE_UNSIGNED_LONG:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- mByteBuffer.putInt((int) tag.getValueAt(i));
- }
- break;
- case ExifTag.TYPE_RATIONAL:
- case ExifTag.TYPE_UNSIGNED_RATIONAL:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- Rational v = tag.getRational(i);
- mByteBuffer.putInt((int) v.getNumerator());
- mByteBuffer.putInt((int) v.getDenominator());
- }
- break;
- case ExifTag.TYPE_UNDEFINED:
- case ExifTag.TYPE_UNSIGNED_BYTE:
- buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
- mByteBuffer.put(buf);
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- mByteBuffer.putShort((short) tag.getValueAt(i));
- }
- break;
- }
- }
-
- public void modifyTag(ExifTag tag) {
- mTagToModified.addTag(tag);
- }
-}
diff --git a/src/com/android/camera/exif/ExifOutputStream.java b/src/com/android/camera/exif/ExifOutputStream.java
deleted file mode 100644
index 191e8280c..000000000
--- a/src/com/android/camera/exif/ExifOutputStream.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-
-/**
- * This class provides a way to replace the Exif header of a JPEG image.
- * <p>
- * Below is an example of writing EXIF data into a file
- *
- * <pre>
- * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
- * OutputStream os = null;
- * try {
- * os = new FileOutputStream(path);
- * ExifOutputStream eos = new ExifOutputStream(os);
- * // Set the exif header
- * eos.setExifData(exif);
- * // Write the original jpeg out, the header will be add into the file.
- * eos.write(jpeg);
- * } catch (FileNotFoundException e) {
- * e.printStackTrace();
- * } catch (IOException e) {
- * e.printStackTrace();
- * } finally {
- * if (os != null) {
- * try {
- * os.close();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * }
- * }
- * }
- * </pre>
- */
-class ExifOutputStream extends FilterOutputStream {
- private static final String TAG = "ExifOutputStream";
- private static final boolean DEBUG = false;
- private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
-
- private static final int STATE_SOI = 0;
- private static final int STATE_FRAME_HEADER = 1;
- private static final int STATE_JPEG_DATA = 2;
-
- private static final int EXIF_HEADER = 0x45786966;
- private static final short TIFF_HEADER = 0x002A;
- private static final short TIFF_BIG_ENDIAN = 0x4d4d;
- private static final short TIFF_LITTLE_ENDIAN = 0x4949;
- private static final short TAG_SIZE = 12;
- private static final short TIFF_HEADER_SIZE = 8;
- private static final int MAX_EXIF_SIZE = 65535;
-
- private ExifData mExifData;
- private int mState = STATE_SOI;
- private int mByteToSkip;
- private int mByteToCopy;
- private byte[] mSingleByteArray = new byte[1];
- private ByteBuffer mBuffer = ByteBuffer.allocate(4);
- private final ExifInterface mInterface;
-
- protected ExifOutputStream(OutputStream ou, ExifInterface iRef) {
- super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
- mInterface = iRef;
- }
-
- /**
- * Sets the ExifData to be written into the JPEG file. Should be called
- * before writing image data.
- */
- protected void setExifData(ExifData exifData) {
- mExifData = exifData;
- }
-
- /**
- * Gets the Exif header to be written into the JPEF file.
- */
- protected ExifData getExifData() {
- return mExifData;
- }
-
- private int requestByteToBuffer(int requestByteCount, byte[] buffer
- , int offset, int length) {
- int byteNeeded = requestByteCount - mBuffer.position();
- int byteToRead = length > byteNeeded ? byteNeeded : length;
- mBuffer.put(buffer, offset, byteToRead);
- return byteToRead;
- }
-
- /**
- * Writes the image out. The input data should be a valid JPEG format. After
- * writing, it's Exif header will be replaced by the given header.
- */
- @Override
- public void write(byte[] buffer, int offset, int length) throws IOException {
- while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
- && length > 0) {
- if (mByteToSkip > 0) {
- int byteToProcess = length > mByteToSkip ? mByteToSkip : length;
- length -= byteToProcess;
- mByteToSkip -= byteToProcess;
- offset += byteToProcess;
- }
- if (mByteToCopy > 0) {
- int byteToProcess = length > mByteToCopy ? mByteToCopy : length;
- out.write(buffer, offset, byteToProcess);
- length -= byteToProcess;
- mByteToCopy -= byteToProcess;
- offset += byteToProcess;
- }
- if (length == 0) {
- return;
- }
- switch (mState) {
- case STATE_SOI:
- int byteRead = requestByteToBuffer(2, buffer, offset, length);
- offset += byteRead;
- length -= byteRead;
- if (mBuffer.position() < 2) {
- return;
- }
- mBuffer.rewind();
- if (mBuffer.getShort() != JpegHeader.SOI) {
- throw new IOException("Not a valid jpeg image, cannot write exif");
- }
- out.write(mBuffer.array(), 0, 2);
- mState = STATE_FRAME_HEADER;
- mBuffer.rewind();
- writeExifData();
- break;
- case STATE_FRAME_HEADER:
- // We ignore the APP1 segment and copy all other segments
- // until SOF tag.
- byteRead = requestByteToBuffer(4, buffer, offset, length);
- offset += byteRead;
- length -= byteRead;
- // Check if this image data doesn't contain SOF.
- if (mBuffer.position() == 2) {
- short tag = mBuffer.getShort();
- if (tag == JpegHeader.EOI) {
- out.write(mBuffer.array(), 0, 2);
- mBuffer.rewind();
- }
- }
- if (mBuffer.position() < 4) {
- return;
- }
- mBuffer.rewind();
- short marker = mBuffer.getShort();
- if (marker == JpegHeader.APP1) {
- mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
- mState = STATE_JPEG_DATA;
- } else if (!JpegHeader.isSofMarker(marker)) {
- out.write(mBuffer.array(), 0, 4);
- mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
- } else {
- out.write(mBuffer.array(), 0, 4);
- mState = STATE_JPEG_DATA;
- }
- mBuffer.rewind();
- }
- }
- if (length > 0) {
- out.write(buffer, offset, length);
- }
- }
-
- /**
- * Writes the one bytes out. The input data should be a valid JPEG format.
- * After writing, it's Exif header will be replaced by the given header.
- */
- @Override
- public void write(int oneByte) throws IOException {
- mSingleByteArray[0] = (byte) (0xff & oneByte);
- write(mSingleByteArray);
- }
-
- /**
- * Equivalent to calling write(buffer, 0, buffer.length).
- */
- @Override
- public void write(byte[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- private void writeExifData() throws IOException {
- if (mExifData == null) {
- return;
- }
- if (DEBUG) {
- Log.v(TAG, "Writing exif data...");
- }
- ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData);
- createRequiredIfdAndTag();
- int exifSize = calculateAllOffset();
- if (exifSize + 8 > MAX_EXIF_SIZE) {
- throw new IOException("Exif header is too large (>64Kb)");
- }
- OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out);
- dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
- dataOutputStream.writeShort(JpegHeader.APP1);
- dataOutputStream.writeShort((short) (exifSize + 8));
- dataOutputStream.writeInt(EXIF_HEADER);
- dataOutputStream.writeShort((short) 0x0000);
- if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) {
- dataOutputStream.writeShort(TIFF_BIG_ENDIAN);
- } else {
- dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN);
- }
- dataOutputStream.setByteOrder(mExifData.getByteOrder());
- dataOutputStream.writeShort(TIFF_HEADER);
- dataOutputStream.writeInt(8);
- writeAllTags(dataOutputStream);
- writeThumbnail(dataOutputStream);
- for (ExifTag t : nullTags) {
- mExifData.addTag(t);
- }
- }
-
- private ArrayList<ExifTag> stripNullValueTags(ExifData data) {
- ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>();
- for(ExifTag t : data.getAllTags()) {
- if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) {
- data.removeTag(t.getTagId(), t.getIfd());
- nullTags.add(t);
- }
- }
- return nullTags;
- }
-
- private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException {
- if (mExifData.hasCompressedThumbnail()) {
- dataOutputStream.write(mExifData.getCompressedThumbnail());
- } else if (mExifData.hasUncompressedStrip()) {
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- dataOutputStream.write(mExifData.getStrip(i));
- }
- }
- }
-
- private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException {
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream);
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream);
- IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interoperabilityIfd != null) {
- writeIfd(interoperabilityIfd, dataOutputStream);
- }
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- writeIfd(gpsIfd, dataOutputStream);
- }
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
- if (ifd1 != null) {
- writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream);
- }
- }
-
- private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream)
- throws IOException {
- ExifTag[] tags = ifd.getAllTags();
- dataOutputStream.writeShort((short) tags.length);
- for (ExifTag tag : tags) {
- dataOutputStream.writeShort(tag.getTagId());
- dataOutputStream.writeShort(tag.getDataType());
- dataOutputStream.writeInt(tag.getComponentCount());
- if (DEBUG) {
- Log.v(TAG, "\n" + tag.toString());
- }
- if (tag.getDataSize() > 4) {
- dataOutputStream.writeInt(tag.getOffset());
- } else {
- ExifOutputStream.writeTagValue(tag, dataOutputStream);
- for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) {
- dataOutputStream.write(0);
- }
- }
- }
- dataOutputStream.writeInt(ifd.getOffsetToNextIfd());
- for (ExifTag tag : tags) {
- if (tag.getDataSize() > 4) {
- ExifOutputStream.writeTagValue(tag, dataOutputStream);
- }
- }
- }
-
- private int calculateOffsetOfIfd(IfdData ifd, int offset) {
- offset += 2 + ifd.getTagCount() * TAG_SIZE + 4;
- ExifTag[] tags = ifd.getAllTags();
- for (ExifTag tag : tags) {
- if (tag.getDataSize() > 4) {
- tag.setOffset(offset);
- offset += tag.getDataSize();
- }
- }
- return offset;
- }
-
- private void createRequiredIfdAndTag() throws IOException {
- // IFD0 is required for all file
- IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
- if (ifd0 == null) {
- ifd0 = new IfdData(IfdId.TYPE_IFD_0);
- mExifData.addIfdData(ifd0);
- }
- ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD);
- if (exifOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_EXIF_IFD);
- }
- ifd0.setTag(exifOffsetTag);
-
- // Exif IFD is required for all files.
- IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
- if (exifIfd == null) {
- exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF);
- mExifData.addIfdData(exifIfd);
- }
-
- // GPS IFD
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD);
- if (gpsOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_GPS_IFD);
- }
- ifd0.setTag(gpsOffsetTag);
- }
-
- // Interoperability IFD
- IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interIfd != null) {
- ExifTag interOffsetTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD);
- if (interOffsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_INTEROPERABILITY_IFD);
- }
- exifIfd.setTag(interOffsetTag);
- }
-
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
-
- // thumbnail
- if (mExifData.hasCompressedThumbnail()) {
-
- if (ifd1 == null) {
- ifd1 = new IfdData(IfdId.TYPE_IFD_1);
- mExifData.addIfdData(ifd1);
- }
-
- ExifTag offsetTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- if (offsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- }
-
- ifd1.setTag(offsetTag);
- ExifTag lengthTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- if (lengthTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- }
-
- lengthTag.setValue(mExifData.getCompressedThumbnail().length);
- ifd1.setTag(lengthTag);
-
- // Get rid of tags for uncompressed if they exist.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
- } else if (mExifData.hasUncompressedStrip()) {
- if (ifd1 == null) {
- ifd1 = new IfdData(IfdId.TYPE_IFD_1);
- mExifData.addIfdData(ifd1);
- }
- int stripCount = mExifData.getStripCount();
- ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS);
- if (offsetTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_STRIP_OFFSETS);
- }
- ExifTag lengthTag = mInterface
- .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS);
- if (lengthTag == null) {
- throw new IOException("No definition for crucial exif tag: "
- + ExifInterface.TAG_STRIP_BYTE_COUNTS);
- }
- long[] lengths = new long[stripCount];
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- lengths[i] = mExifData.getStrip(i).length;
- }
- lengthTag.setValue(lengths);
- ifd1.setTag(offsetTag);
- ifd1.setTag(lengthTag);
- // Get rid of tags for compressed if they exist.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
- ifd1.removeTag(ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- } else if (ifd1 != null) {
- // Get rid of offset and length tags if there is no thumbnail.
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
- ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
- ifd1.removeTag(ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
- }
- }
-
- private int calculateAllOffset() {
- int offset = TIFF_HEADER_SIZE;
- IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
- offset = calculateOffsetOfIfd(ifd0, offset);
- ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset);
-
- IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
- offset = calculateOffsetOfIfd(exifIfd, offset);
-
- IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
- if (interIfd != null) {
- exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
- .setValue(offset);
- offset = calculateOffsetOfIfd(interIfd, offset);
- }
-
- IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
- if (gpsIfd != null) {
- ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset);
- offset = calculateOffsetOfIfd(gpsIfd, offset);
- }
-
- IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
- if (ifd1 != null) {
- ifd0.setOffsetToNextIfd(offset);
- offset = calculateOffsetOfIfd(ifd1, offset);
- }
-
- // thumbnail
- if (mExifData.hasCompressedThumbnail()) {
- ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
- .setValue(offset);
- offset += mExifData.getCompressedThumbnail().length;
- } else if (mExifData.hasUncompressedStrip()) {
- int stripCount = mExifData.getStripCount();
- long[] offsets = new long[stripCount];
- for (int i = 0; i < mExifData.getStripCount(); i++) {
- offsets[i] = offset;
- offset += mExifData.getStrip(i).length;
- }
- ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue(
- offsets);
- }
- return offset;
- }
-
- static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream)
- throws IOException {
- switch (tag.getDataType()) {
- case ExifTag.TYPE_ASCII:
- byte buf[] = tag.getStringByte();
- if (buf.length == tag.getComponentCount()) {
- buf[buf.length - 1] = 0;
- dataOutputStream.write(buf);
- } else {
- dataOutputStream.write(buf);
- dataOutputStream.write(0);
- }
- break;
- case ExifTag.TYPE_LONG:
- case ExifTag.TYPE_UNSIGNED_LONG:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeInt((int) tag.getValueAt(i));
- }
- break;
- case ExifTag.TYPE_RATIONAL:
- case ExifTag.TYPE_UNSIGNED_RATIONAL:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeRational(tag.getRational(i));
- }
- break;
- case ExifTag.TYPE_UNDEFINED:
- case ExifTag.TYPE_UNSIGNED_BYTE:
- buf = new byte[tag.getComponentCount()];
- tag.getBytes(buf);
- dataOutputStream.write(buf);
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT:
- for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
- dataOutputStream.writeShort((short) tag.getValueAt(i));
- }
- break;
- }
- }
-}
diff --git a/src/com/android/camera/exif/ExifParser.java b/src/com/android/camera/exif/ExifParser.java
deleted file mode 100644
index 766268b1c..000000000
--- a/src/com/android/camera/exif/ExifParser.java
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-
-/**
- * This class provides a low-level EXIF parsing API. Given a JPEG format
- * InputStream, the caller can request which IFD's to read via
- * {@link #parse(InputStream, int)} with given options.
- * <p>
- * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
- * parser.
- *
- * <pre>
- * void parse() {
- * ExifParser parser = ExifParser.parse(mImageInputStream,
- * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
- * int event = parser.next();
- * while (event != ExifParser.EVENT_END) {
- * switch (event) {
- * case ExifParser.EVENT_START_OF_IFD:
- * break;
- * case ExifParser.EVENT_NEW_TAG:
- * ExifTag tag = parser.getTag();
- * if (!tag.hasValue()) {
- * parser.registerForTagValue(tag);
- * } else {
- * processTag(tag);
- * }
- * break;
- * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
- * tag = parser.getTag();
- * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
- * processTag(tag);
- * }
- * break;
- * }
- * event = parser.next();
- * }
- * }
- *
- * void processTag(ExifTag tag) {
- * // process the tag as you like.
- * }
- * </pre>
- */
-class ExifParser {
- private static final boolean LOGV = false;
- private static final String TAG = "ExifParser";
- /**
- * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
- * know which IFD we are in.
- */
- public static final int EVENT_START_OF_IFD = 0;
- /**
- * When the parser reaches a new tag. Call {@link #getTag()}to get the
- * corresponding tag.
- */
- public static final int EVENT_NEW_TAG = 1;
- /**
- * When the parser reaches the value area of tag that is registered by
- * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
- * to get the corresponding tag.
- */
- public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
-
- /**
- * When the parser reaches the compressed image area.
- */
- public static final int EVENT_COMPRESSED_IMAGE = 3;
- /**
- * When the parser reaches the uncompressed image strip. Call
- * {@link #getStripIndex()} to get the index of the strip.
- *
- * @see #getStripIndex()
- * @see #getStripCount()
- */
- public static final int EVENT_UNCOMPRESSED_STRIP = 4;
- /**
- * When there is nothing more to parse.
- */
- public static final int EVENT_END = 5;
-
- /**
- * Option bit to request to parse IFD0.
- */
- public static final int OPTION_IFD_0 = 1 << 0;
- /**
- * Option bit to request to parse IFD1.
- */
- public static final int OPTION_IFD_1 = 1 << 1;
- /**
- * Option bit to request to parse Exif-IFD.
- */
- public static final int OPTION_IFD_EXIF = 1 << 2;
- /**
- * Option bit to request to parse GPS-IFD.
- */
- public static final int OPTION_IFD_GPS = 1 << 3;
- /**
- * Option bit to request to parse Interoperability-IFD.
- */
- public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
- /**
- * Option bit to request to parse thumbnail.
- */
- public static final int OPTION_THUMBNAIL = 1 << 5;
-
- protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
- protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
-
- // TIFF header
- protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
- protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
- protected static final short TIFF_HEADER_TAIL = 0x002A;
-
- protected static final int TAG_SIZE = 12;
- protected static final int OFFSET_SIZE = 2;
-
- private static final Charset US_ASCII = Charset.forName("US-ASCII");
-
- protected static final int DEFAULT_IFD0_OFFSET = 8;
-
- private final CountedDataInputStream mTiffStream;
- private final int mOptions;
- private int mIfdStartOffset = 0;
- private int mNumOfTagInIfd = 0;
- private int mIfdType;
- private ExifTag mTag;
- private ImageEvent mImageEvent;
- private int mStripCount;
- private ExifTag mStripSizeTag;
- private ExifTag mJpegSizeTag;
- private boolean mNeedToParseOffsetsInCurrentIfd;
- private boolean mContainExifData = false;
- private int mApp1End;
- private int mOffsetToApp1EndFromSOF = 0;
- private byte[] mDataAboveIfd0;
- private int mIfd0Position;
- private int mTiffStartPosition;
- private final ExifInterface mInterface;
-
- private static final short TAG_EXIF_IFD = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
- private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
- private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
- private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
- private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
- private static final short TAG_STRIP_OFFSETS = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
- private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
- .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
-
- private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
-
- private boolean isIfdRequested(int ifdType) {
- switch (ifdType) {
- case IfdId.TYPE_IFD_0:
- return (mOptions & OPTION_IFD_0) != 0;
- case IfdId.TYPE_IFD_1:
- return (mOptions & OPTION_IFD_1) != 0;
- case IfdId.TYPE_IFD_EXIF:
- return (mOptions & OPTION_IFD_EXIF) != 0;
- case IfdId.TYPE_IFD_GPS:
- return (mOptions & OPTION_IFD_GPS) != 0;
- case IfdId.TYPE_IFD_INTEROPERABILITY:
- return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
- }
- return false;
- }
-
- private boolean isThumbnailRequested() {
- return (mOptions & OPTION_THUMBNAIL) != 0;
- }
-
- private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- if (inputStream == null) {
- throw new IOException("Null argument inputStream to ExifParser");
- }
- if (LOGV) {
- Log.v(TAG, "Reading exif...");
- }
- mInterface = iRef;
- mContainExifData = seekTiffData(inputStream);
- mTiffStream = new CountedDataInputStream(inputStream);
- mOptions = options;
- if (!mContainExifData) {
- return;
- }
-
- parseTiffHeader();
- long offset = mTiffStream.readUnsignedInt();
- if (offset > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException("Invalid offset " + offset);
- }
- mIfd0Position = (int) offset;
- mIfdType = IfdId.TYPE_IFD_0;
- if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
- registerIfd(IfdId.TYPE_IFD_0, offset);
- if (offset != DEFAULT_IFD0_OFFSET) {
- mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
- read(mDataAboveIfd0);
- }
- }
- }
-
- /**
- * Parses the the given InputStream with the given options
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- */
- protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- return new ExifParser(inputStream, options, iRef);
- }
-
- /**
- * Parses the the given InputStream with default options; that is, every IFD
- * and thumbnaill will be parsed.
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- * @see #parse(InputStream, int)
- */
- protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
- throws IOException, ExifInvalidFormatException {
- return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
- | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
- | OPTION_THUMBNAIL, iRef);
- }
-
- /**
- * Moves the parser forward and returns the next parsing event
- *
- * @exception IOException
- * @exception ExifInvalidFormatException
- * @see #EVENT_START_OF_IFD
- * @see #EVENT_NEW_TAG
- * @see #EVENT_VALUE_OF_REGISTERED_TAG
- * @see #EVENT_COMPRESSED_IMAGE
- * @see #EVENT_UNCOMPRESSED_STRIP
- * @see #EVENT_END
- */
- protected int next() throws IOException, ExifInvalidFormatException {
- if (!mContainExifData) {
- return EVENT_END;
- }
- int offset = mTiffStream.getReadByteCount();
- int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
- if (offset < endOfTags) {
- mTag = readTag();
- if (mTag == null) {
- return next();
- }
- if (mNeedToParseOffsetsInCurrentIfd) {
- checkOffsetOrImageTag(mTag);
- }
- return EVENT_NEW_TAG;
- } else if (offset == endOfTags) {
- // There is a link to ifd1 at the end of ifd0
- if (mIfdType == IfdId.TYPE_IFD_0) {
- long ifdOffset = readUnsignedLong();
- if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
- if (ifdOffset != 0) {
- registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
- }
- }
- } else {
- int offsetSize = 4;
- // Some camera models use invalid length of the offset
- if (mCorrespondingEvent.size() > 0) {
- offsetSize = mCorrespondingEvent.firstEntry().getKey() -
- mTiffStream.getReadByteCount();
- }
- if (offsetSize < 4) {
- Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
- } else {
- long ifdOffset = readUnsignedLong();
- if (ifdOffset != 0) {
- Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
- }
- }
- }
- }
- while (mCorrespondingEvent.size() != 0) {
- Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
- Object event = entry.getValue();
- try {
- skipTo(entry.getKey());
- } catch (IOException e) {
- Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
- " for " + event.getClass().getName() + ", the file may be broken.");
- continue;
- }
- if (event instanceof IfdEvent) {
- mIfdType = ((IfdEvent) event).ifd;
- mNumOfTagInIfd = mTiffStream.readUnsignedShort();
- mIfdStartOffset = entry.getKey();
-
- if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
- Log.w(TAG, "Invalid size of IFD " + mIfdType);
- return EVENT_END;
- }
-
- mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
- if (((IfdEvent) event).isRequested) {
- return EVENT_START_OF_IFD;
- } else {
- skipRemainingTagsInCurrentIfd();
- }
- } else if (event instanceof ImageEvent) {
- mImageEvent = (ImageEvent) event;
- return mImageEvent.type;
- } else {
- ExifTagEvent tagEvent = (ExifTagEvent) event;
- mTag = tagEvent.tag;
- if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
- readFullTagValue(mTag);
- checkOffsetOrImageTag(mTag);
- }
- if (tagEvent.isRequested) {
- return EVENT_VALUE_OF_REGISTERED_TAG;
- }
- }
- }
- return EVENT_END;
- }
-
- /**
- * Skips the tags area of current IFD, if the parser is not in the tag area,
- * nothing will happen.
- *
- * @throws IOException
- * @throws ExifInvalidFormatException
- */
- protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
- int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
- int offset = mTiffStream.getReadByteCount();
- if (offset > endOfTags) {
- return;
- }
- if (mNeedToParseOffsetsInCurrentIfd) {
- while (offset < endOfTags) {
- mTag = readTag();
- offset += TAG_SIZE;
- if (mTag == null) {
- continue;
- }
- checkOffsetOrImageTag(mTag);
- }
- } else {
- skipTo(endOfTags);
- }
- long ifdOffset = readUnsignedLong();
- // For ifd0, there is a link to ifd1 in the end of all tags
- if (mIfdType == IfdId.TYPE_IFD_0
- && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
- if (ifdOffset > 0) {
- registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
- }
- }
- }
-
- private boolean needToParseOffsetsInCurrentIfd() {
- switch (mIfdType) {
- case IfdId.TYPE_IFD_0:
- return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
- || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
- || isIfdRequested(IfdId.TYPE_IFD_1);
- case IfdId.TYPE_IFD_1:
- return isThumbnailRequested();
- case IfdId.TYPE_IFD_EXIF:
- // The offset to interoperability IFD is located in Exif IFD
- return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
- default:
- return false;
- }
- }
-
- /**
- * If {@link #next()} return {@link #EVENT_NEW_TAG} or
- * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
- * corresponding tag.
- * <p>
- * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
- * of the value is greater than 4 bytes. One should call
- * {@link ExifTag#hasValue()} to check if the tag contains value. If there
- * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
- * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
- * pointed by the offset.
- * <p>
- * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
- * tag will have already been read except for tags of undefined type. For
- * tags of undefined type, call one of the read methods to get the value.
- *
- * @see #registerForTagValue(ExifTag)
- * @see #read(byte[])
- * @see #read(byte[], int, int)
- * @see #readLong()
- * @see #readRational()
- * @see #readString(int)
- * @see #readString(int, Charset)
- */
- protected ExifTag getTag() {
- return mTag;
- }
-
- /**
- * Gets number of tags in the current IFD area.
- */
- protected int getTagCountInCurrentIfd() {
- return mNumOfTagInIfd;
- }
-
- /**
- * Gets the ID of current IFD.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- * @see IfdId#TYPE_IFD_EXIF
- */
- protected int getCurrentIfd() {
- return mIfdType;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the index of this strip.
- *
- * @see #getStripCount()
- */
- protected int getStripIndex() {
- return mImageEvent.stripIndex;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the number of strip data.
- *
- * @see #getStripIndex()
- */
- protected int getStripCount() {
- return mStripCount;
- }
-
- /**
- * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
- * get the strip size.
- */
- protected int getStripSize() {
- if (mStripSizeTag == null)
- return 0;
- return (int) mStripSizeTag.getValueAt(0);
- }
-
- /**
- * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
- * the image data size.
- */
- protected int getCompressedImageSize() {
- if (mJpegSizeTag == null) {
- return 0;
- }
- return (int) mJpegSizeTag.getValueAt(0);
- }
-
- private void skipTo(int offset) throws IOException {
- mTiffStream.skipTo(offset);
- while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
- mCorrespondingEvent.pollFirstEntry();
- }
- }
-
- /**
- * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
- * not contain the value if the size of the value is greater than 4 bytes.
- * When the value is not available here, call this method so that the parser
- * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
- * where the value is located.
- *
- * @see #EVENT_VALUE_OF_REGISTERED_TAG
- */
- protected void registerForTagValue(ExifTag tag) {
- if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
- mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
- }
- }
-
- private void registerIfd(int ifdType, long offset) {
- // Cast unsigned int to int since the offset is always smaller
- // than the size of APP1 (65536)
- mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
- }
-
- private void registerCompressedImage(long offset) {
- mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
- }
-
- private void registerUncompressedStrip(int stripIndex, long offset) {
- mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
- , stripIndex));
- }
-
- private ExifTag readTag() throws IOException, ExifInvalidFormatException {
- short tagId = mTiffStream.readShort();
- short dataFormat = mTiffStream.readShort();
- long numOfComp = mTiffStream.readUnsignedInt();
- if (numOfComp > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException(
- "Number of component is larger then Integer.MAX_VALUE");
- }
- // Some invalid image file contains invalid data type. Ignore those tags
- if (!ExifTag.isValidType(dataFormat)) {
- Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
- mTiffStream.skip(4);
- return null;
- }
- // TODO: handle numOfComp overflow
- ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
- ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
- int dataSize = tag.getDataSize();
- if (dataSize > 4) {
- long offset = mTiffStream.readUnsignedInt();
- if (offset > Integer.MAX_VALUE) {
- throw new ExifInvalidFormatException(
- "offset is larger then Integer.MAX_VALUE");
- }
- // Some invalid images put some undefined data before IFD0.
- // Read the data here.
- if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
- byte[] buf = new byte[(int) numOfComp];
- System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
- buf, 0, (int) numOfComp);
- tag.setValue(buf);
- } else {
- tag.setOffset((int) offset);
- }
- } else {
- boolean defCount = tag.hasDefinedCount();
- // Set defined count to 0 so we can add \0 to non-terminated strings
- tag.setHasDefinedCount(false);
- // Read value
- readFullTagValue(tag);
- tag.setHasDefinedCount(defCount);
- mTiffStream.skip(4 - dataSize);
- // Set the offset to the position of value.
- tag.setOffset(mTiffStream.getReadByteCount() - 4);
- }
- return tag;
- }
-
- /**
- * Check the tag, if the tag is one of the offset tag that points to the IFD
- * or image the caller is interested in, register the IFD or image.
- */
- private void checkOffsetOrImageTag(ExifTag tag) {
- // Some invalid formattd image contains tag with 0 size.
- if (tag.getComponentCount() == 0) {
- return;
- }
- short tid = tag.getTagId();
- int ifd = tag.getIfd();
- if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
- || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
- registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
- }
- } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
- registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
- }
- } else if (tid == TAG_INTEROPERABILITY_IFD
- && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
- if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
- registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
- }
- } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
- && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
- if (isThumbnailRequested()) {
- registerCompressedImage(tag.getValueAt(0));
- }
- } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
- && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
- if (isThumbnailRequested()) {
- mJpegSizeTag = tag;
- }
- } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
- if (isThumbnailRequested()) {
- if (tag.hasValue()) {
- for (int i = 0; i < tag.getComponentCount(); i++) {
- if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
- registerUncompressedStrip(i, tag.getValueAt(i));
- } else {
- registerUncompressedStrip(i, tag.getValueAt(i));
- }
- }
- } else {
- mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
- }
- }
- } else if (tid == TAG_STRIP_BYTE_COUNTS
- && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
- &&isThumbnailRequested() && tag.hasValue()) {
- mStripSizeTag = tag;
- }
- }
-
- private boolean checkAllowed(int ifd, int tagId) {
- int info = mInterface.getTagInfo().get(tagId);
- if (info == ExifInterface.DEFINITION_NULL) {
- return false;
- }
- return ExifInterface.isIfdAllowed(info, ifd);
- }
-
- protected void readFullTagValue(ExifTag tag) throws IOException {
- // Some invalid images contains tags with wrong size, check it here
- short type = tag.getDataType();
- if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
- type == ExifTag.TYPE_UNSIGNED_BYTE) {
- int size = tag.getComponentCount();
- if (mCorrespondingEvent.size() > 0) {
- if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
- + size) {
- Object event = mCorrespondingEvent.firstEntry().getValue();
- if (event instanceof ImageEvent) {
- // Tag value overlaps thumbnail, ignore thumbnail.
- Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
- Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
- Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
- } else {
- // Tag value overlaps another tag, shorten count
- if (event instanceof IfdEvent) {
- Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
- + " overlaps value for tag: \n" + tag.toString());
- } else if (event instanceof ExifTagEvent) {
- Log.w(TAG, "Tag value for tag: \n"
- + ((ExifTagEvent) event).tag.toString()
- + " overlaps value for tag: \n" + tag.toString());
- }
- size = mCorrespondingEvent.firstEntry().getKey()
- - mTiffStream.getReadByteCount();
- Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
- + " setting count to: " + size);
- tag.forceSetComponentCount(size);
- }
- }
- }
- }
- switch (tag.getDataType()) {
- case ExifTag.TYPE_UNSIGNED_BYTE:
- case ExifTag.TYPE_UNDEFINED: {
- byte buf[] = new byte[tag.getComponentCount()];
- read(buf);
- tag.setValue(buf);
- }
- break;
- case ExifTag.TYPE_ASCII:
- tag.setValue(readString(tag.getComponentCount()));
- break;
- case ExifTag.TYPE_UNSIGNED_LONG: {
- long value[] = new long[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedLong();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_UNSIGNED_RATIONAL: {
- Rational value[] = new Rational[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedRational();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_UNSIGNED_SHORT: {
- int value[] = new int[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readUnsignedShort();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_LONG: {
- int value[] = new int[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readLong();
- }
- tag.setValue(value);
- }
- break;
- case ExifTag.TYPE_RATIONAL: {
- Rational value[] = new Rational[tag.getComponentCount()];
- for (int i = 0, n = value.length; i < n; i++) {
- value[i] = readRational();
- }
- tag.setValue(value);
- }
- break;
- }
- if (LOGV) {
- Log.v(TAG, "\n" + tag.toString());
- }
- }
-
- private void parseTiffHeader() throws IOException,
- ExifInvalidFormatException {
- short byteOrder = mTiffStream.readShort();
- if (LITTLE_ENDIAN_TAG == byteOrder) {
- mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
- } else if (BIG_ENDIAN_TAG == byteOrder) {
- mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
- } else {
- throw new ExifInvalidFormatException("Invalid TIFF header");
- }
-
- if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
- throw new ExifInvalidFormatException("Invalid TIFF header");
- }
- }
-
- private boolean seekTiffData(InputStream inputStream) throws IOException,
- ExifInvalidFormatException {
- CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
- if (dataStream.readShort() != JpegHeader.SOI) {
- throw new ExifInvalidFormatException("Invalid JPEG format");
- }
-
- short marker = dataStream.readShort();
- while (marker != JpegHeader.EOI
- && !JpegHeader.isSofMarker(marker)) {
- int length = dataStream.readUnsignedShort();
- // Some invalid formatted image contains multiple APP1,
- // try to find the one with Exif data.
- if (marker == JpegHeader.APP1) {
- int header = 0;
- short headerTail = 0;
- if (length >= 8) {
- header = dataStream.readInt();
- headerTail = dataStream.readShort();
- length -= 6;
- if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
- mTiffStartPosition = dataStream.getReadByteCount();
- mApp1End = length;
- mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
- return true;
- }
- }
- }
- if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
- Log.w(TAG, "Invalid JPEG format.");
- return false;
- }
- marker = dataStream.readShort();
- }
- return false;
- }
-
- protected int getOffsetToExifEndFromSOF() {
- return mOffsetToApp1EndFromSOF;
- }
-
- protected int getTiffStartPosition() {
- return mTiffStartPosition;
- }
-
- /**
- * Reads bytes from the InputStream.
- */
- protected int read(byte[] buffer, int offset, int length) throws IOException {
- return mTiffStream.read(buffer, offset, length);
- }
-
- /**
- * Equivalent to read(buffer, 0, buffer.length).
- */
- protected int read(byte[] buffer) throws IOException {
- return mTiffStream.read(buffer);
- }
-
- /**
- * Reads a String from the InputStream with US-ASCII charset. The parser
- * will read n bytes and convert it to ascii string. This is used for
- * reading values of type {@link ExifTag#TYPE_ASCII}.
- */
- protected String readString(int n) throws IOException {
- return readString(n, US_ASCII);
- }
-
- /**
- * Reads a String from the InputStream with the given charset. The parser
- * will read n bytes and convert it to string. This is used for reading
- * values of type {@link ExifTag#TYPE_ASCII}.
- */
- protected String readString(int n, Charset charset) throws IOException {
- if (n > 0) {
- return mTiffStream.readString(n, charset);
- } else {
- return "";
- }
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
- * InputStream.
- */
- protected int readUnsignedShort() throws IOException {
- return mTiffStream.readShort() & 0xffff;
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
- * InputStream.
- */
- protected long readUnsignedLong() throws IOException {
- return readLong() & 0xffffffffL;
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
- * InputStream.
- */
- protected Rational readUnsignedRational() throws IOException {
- long nomi = readUnsignedLong();
- long denomi = readUnsignedLong();
- return new Rational(nomi, denomi);
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
- */
- protected int readLong() throws IOException {
- return mTiffStream.readInt();
- }
-
- /**
- * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
- */
- protected Rational readRational() throws IOException {
- int nomi = readLong();
- int denomi = readLong();
- return new Rational(nomi, denomi);
- }
-
- private static class ImageEvent {
- int stripIndex;
- int type;
-
- ImageEvent(int type) {
- this.stripIndex = 0;
- this.type = type;
- }
-
- ImageEvent(int type, int stripIndex) {
- this.type = type;
- this.stripIndex = stripIndex;
- }
- }
-
- private static class IfdEvent {
- int ifd;
- boolean isRequested;
-
- IfdEvent(int ifd, boolean isInterestedIfd) {
- this.ifd = ifd;
- this.isRequested = isInterestedIfd;
- }
- }
-
- private static class ExifTagEvent {
- ExifTag tag;
- boolean isRequested;
-
- ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
- this.tag = tag;
- this.isRequested = isRequireByUser;
- }
- }
-
- /**
- * Gets the byte order of the current InputStream.
- */
- protected ByteOrder getByteOrder() {
- return mTiffStream.getByteOrder();
- }
-}
diff --git a/src/com/android/camera/exif/ExifReader.java b/src/com/android/camera/exif/ExifReader.java
deleted file mode 100644
index 757e0d329..000000000
--- a/src/com/android/camera/exif/ExifReader.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This class reads the EXIF header of a JPEG file and stores it in
- * {@link ExifData}.
- */
-class ExifReader {
- private static final String TAG = "ExifReader";
-
- private final ExifInterface mInterface;
-
- ExifReader(ExifInterface iRef) {
- mInterface = iRef;
- }
-
- /**
- * Parses the inputStream and and returns the EXIF data in an
- * {@link ExifData}.
- *
- * @throws ExifInvalidFormatException
- * @throws IOException
- */
- protected ExifData read(InputStream inputStream) throws ExifInvalidFormatException,
- IOException {
- ExifParser parser = ExifParser.parse(inputStream, mInterface);
- ExifData exifData = new ExifData(parser.getByteOrder());
- ExifTag tag = null;
-
- int event = parser.next();
- while (event != ExifParser.EVENT_END) {
- switch (event) {
- case ExifParser.EVENT_START_OF_IFD:
- exifData.addIfdData(new IfdData(parser.getCurrentIfd()));
- break;
- case ExifParser.EVENT_NEW_TAG:
- tag = parser.getTag();
- if (!tag.hasValue()) {
- parser.registerForTagValue(tag);
- } else {
- exifData.getIfdData(tag.getIfd()).setTag(tag);
- }
- break;
- case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
- tag = parser.getTag();
- if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
- parser.readFullTagValue(tag);
- }
- exifData.getIfdData(tag.getIfd()).setTag(tag);
- break;
- case ExifParser.EVENT_COMPRESSED_IMAGE:
- byte buf[] = new byte[parser.getCompressedImageSize()];
- if (buf.length == parser.read(buf)) {
- exifData.setCompressedThumbnail(buf);
- } else {
- Log.w(TAG, "Failed to read the compressed thumbnail");
- }
- break;
- case ExifParser.EVENT_UNCOMPRESSED_STRIP:
- buf = new byte[parser.getStripSize()];
- if (buf.length == parser.read(buf)) {
- exifData.setStripBytes(parser.getStripIndex(), buf);
- } else {
- Log.w(TAG, "Failed to read the strip bytes");
- }
- break;
- }
- event = parser.next();
- }
- return exifData;
- }
-}
diff --git a/src/com/android/camera/exif/ExifTag.java b/src/com/android/camera/exif/ExifTag.java
deleted file mode 100644
index 1d50316dd..000000000
--- a/src/com/android/camera/exif/ExifTag.java
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-
-/**
- * This class stores information of an EXIF tag. For more information about
- * defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
- * instantiated using {@link ExifInterface#buildTag}.
- *
- * @see ExifInterface
- */
-public class ExifTag {
- /**
- * The BYTE type in the EXIF standard. An 8-bit unsigned integer.
- */
- public static final short TYPE_UNSIGNED_BYTE = 1;
- /**
- * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
- * ASCII code. The final byte is terminated with NULL.
- */
- public static final short TYPE_ASCII = 2;
- /**
- * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
- */
- public static final short TYPE_UNSIGNED_SHORT = 3;
- /**
- * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
- */
- public static final short TYPE_UNSIGNED_LONG = 4;
- /**
- * The RATIONAL type of EXIF standard. It consists of two LONGs. The first
- * one is the numerator and the second one expresses the denominator.
- */
- public static final short TYPE_UNSIGNED_RATIONAL = 5;
- /**
- * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
- * value depending on the field definition.
- */
- public static final short TYPE_UNDEFINED = 7;
- /**
- * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
- * (2's complement notation).
- */
- public static final short TYPE_LONG = 9;
- /**
- * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
- * one is the numerator and the second one is the denominator.
- */
- public static final short TYPE_RATIONAL = 10;
-
- private static Charset US_ASCII = Charset.forName("US-ASCII");
- private static final int TYPE_TO_SIZE_MAP[] = new int[11];
- private static final int UNSIGNED_SHORT_MAX = 65535;
- private static final long UNSIGNED_LONG_MAX = 4294967295L;
- private static final long LONG_MAX = Integer.MAX_VALUE;
- private static final long LONG_MIN = Integer.MIN_VALUE;
-
- static {
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
- TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
- TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
- TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
- TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
- }
-
- static final int SIZE_UNDEFINED = 0;
-
- // Exif TagId
- private final short mTagId;
- // Exif Tag Type
- private final short mDataType;
- // If tag has defined count
- private boolean mHasDefinedDefaultComponentCount;
- // Actual data count in tag (should be number of elements in value array)
- private int mComponentCountActual;
- // The ifd that this tag should be put in
- private int mIfd;
- // The value (array of elements of type Tag Type)
- private Object mValue;
- // Value offset in exif header.
- private int mOffset;
-
- private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss");
-
- /**
- * Returns true if the given IFD is a valid IFD.
- */
- public static boolean isValidIfd(int ifdId) {
- return ifdId == IfdId.TYPE_IFD_0 || ifdId == IfdId.TYPE_IFD_1
- || ifdId == IfdId.TYPE_IFD_EXIF || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY
- || ifdId == IfdId.TYPE_IFD_GPS;
- }
-
- /**
- * Returns true if a given type is a valid tag type.
- */
- public static boolean isValidType(short type) {
- return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII ||
- type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
- type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
- type == TYPE_LONG || type == TYPE_RATIONAL;
- }
-
- // Use builtTag in ExifInterface instead of constructor.
- ExifTag(short tagId, short type, int componentCount, int ifd,
- boolean hasDefinedComponentCount) {
- mTagId = tagId;
- mDataType = type;
- mComponentCountActual = componentCount;
- mHasDefinedDefaultComponentCount = hasDefinedComponentCount;
- mIfd = ifd;
- mValue = null;
- }
-
- /**
- * Gets the element size of the given data type in bytes.
- *
- * @see #TYPE_ASCII
- * @see #TYPE_LONG
- * @see #TYPE_RATIONAL
- * @see #TYPE_UNDEFINED
- * @see #TYPE_UNSIGNED_BYTE
- * @see #TYPE_UNSIGNED_LONG
- * @see #TYPE_UNSIGNED_RATIONAL
- * @see #TYPE_UNSIGNED_SHORT
- */
- public static int getElementSize(short type) {
- return TYPE_TO_SIZE_MAP[type];
- }
-
- /**
- * Returns the ID of the IFD this tag belongs to.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- public int getIfd() {
- return mIfd;
- }
-
- protected void setIfd(int ifdId) {
- mIfd = ifdId;
- }
-
- /**
- * Gets the TID of this tag.
- */
- public short getTagId() {
- return mTagId;
- }
-
- /**
- * Gets the data type of this tag
- *
- * @see #TYPE_ASCII
- * @see #TYPE_LONG
- * @see #TYPE_RATIONAL
- * @see #TYPE_UNDEFINED
- * @see #TYPE_UNSIGNED_BYTE
- * @see #TYPE_UNSIGNED_LONG
- * @see #TYPE_UNSIGNED_RATIONAL
- * @see #TYPE_UNSIGNED_SHORT
- */
- public short getDataType() {
- return mDataType;
- }
-
- /**
- * Gets the total data size in bytes of the value of this tag.
- */
- public int getDataSize() {
- return getComponentCount() * getElementSize(getDataType());
- }
-
- /**
- * Gets the component count of this tag.
- */
-
- // TODO: fix integer overflows with this
- public int getComponentCount() {
- return mComponentCountActual;
- }
-
- /**
- * Sets the component count of this tag. Call this function before
- * setValue() if the length of value does not match the component count.
- */
- protected void forceSetComponentCount(int count) {
- mComponentCountActual = count;
- }
-
- /**
- * Returns true if this ExifTag contains value; otherwise, this tag will
- * contain an offset value that is determined when the tag is written.
- */
- public boolean hasValue() {
- return mValue != null;
- }
-
- /**
- * Sets integer values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_SHORT}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- */
- public boolean setValue(int[] value) {
- if (checkBadComponentCount(value.length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG &&
- mDataType != TYPE_UNSIGNED_LONG) {
- return false;
- }
- if (mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
- return false;
- } else if (mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
- return false;
- }
-
- long[] data = new long[value.length];
- for (int i = 0; i < value.length; i++) {
- data[i] = value[i];
- }
- mValue = data;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets integer value into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_SHORT}, or {@link #TYPE_LONG}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition of this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(int value) {
- return setValue(new int[] {
- value
- });
- }
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- */
- public boolean setValue(long[] value) {
- if (checkBadComponentCount(value.length) || mDataType != TYPE_UNSIGNED_LONG) {
- return false;
- }
- if (checkOverflowForUnsignedLong(value)) {
- return false;
- }
- mValue = value;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(long value) {
- return setValue(new long[] {
- value
- });
- }
-
- /**
- * Sets a string value into this tag. This method should be used for tags of
- * type {@link #TYPE_ASCII}. The string is converted to an ASCII string.
- * Characters that cannot be converted are replaced with '?'. The length of
- * the string must be equal to either (component count -1) or (component
- * count). The final byte will be set to the string null terminator '\0',
- * overwriting the last character in the string if the value.length is equal
- * to the component count. This method will fail if:
- * <ul>
- * <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.</li>
- * <li>The length of the string is not equal to (component count -1) or
- * (component count) in the definition for this tag.</li>
- * </ul>
- */
- public boolean setValue(String value) {
- if (mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED) {
- return false;
- }
-
- byte[] buf = value.getBytes(US_ASCII);
- byte[] finalBuf = buf;
- if (buf.length > 0) {
- finalBuf = (buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED) ? buf : Arrays
- .copyOf(buf, buf.length + 1);
- } else if (mDataType == TYPE_ASCII && mComponentCountActual == 1) {
- finalBuf = new byte[] { 0 };
- }
- int count = finalBuf.length;
- if (checkBadComponentCount(count)) {
- return false;
- }
- mComponentCountActual = count;
- mValue = finalBuf;
- return true;
- }
-
- /**
- * Sets Rational values into this tag. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
- * method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
- * or {@link #TYPE_RATIONAL}.</li>
- * <li>The value overflows.</li>
- * <li>The value.length does NOT match the component count in the definition
- * for this tag.</li>
- * </ul>
- *
- * @see Rational
- */
- public boolean setValue(Rational[] value) {
- if (checkBadComponentCount(value.length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL) {
- return false;
- }
- if (mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
- return false;
- } else if (mDataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
- return false;
- }
-
- mValue = value;
- mComponentCountActual = value.length;
- return true;
- }
-
- /**
- * Sets a Rational value into this tag. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
- * method will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
- * or {@link #TYPE_RATIONAL}.</li>
- * <li>The value overflows.</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- *
- * @see Rational
- */
- public boolean setValue(Rational value) {
- return setValue(new Rational[] {
- value
- });
- }
-
- /**
- * Sets byte values into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
- * {@link #TYPE_UNDEFINED} .</li>
- * <li>The length does NOT match the component count in the definition for
- * this tag.</li>
- * </ul>
- */
- public boolean setValue(byte[] value, int offset, int length) {
- if (checkBadComponentCount(length)) {
- return false;
- }
- if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) {
- return false;
- }
- mValue = new byte[length];
- System.arraycopy(value, offset, mValue, 0, length);
- mComponentCountActual = length;
- return true;
- }
-
- /**
- * Equivalent to setValue(value, 0, value.length).
- */
- public boolean setValue(byte[] value) {
- return setValue(value, 0, value.length);
- }
-
- /**
- * Sets byte value into this tag. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
- * will fail if:
- * <ul>
- * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
- * {@link #TYPE_UNDEFINED} .</li>
- * <li>The component count in the definition for this tag is not 1.</li>
- * </ul>
- */
- public boolean setValue(byte value) {
- return setValue(new byte[] {
- value
- });
- }
-
- /**
- * Sets the value for this tag using an appropriate setValue method for the
- * given object. This method will fail if:
- * <ul>
- * <li>The corresponding setValue method for the class of the object passed
- * in would fail.</li>
- * <li>There is no obvious way to cast the object passed in into an EXIF tag
- * type.</li>
- * </ul>
- */
- public boolean setValue(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj instanceof Short) {
- return setValue(((Short) obj).shortValue() & 0x0ffff);
- } else if (obj instanceof String) {
- return setValue((String) obj);
- } else if (obj instanceof int[]) {
- return setValue((int[]) obj);
- } else if (obj instanceof long[]) {
- return setValue((long[]) obj);
- } else if (obj instanceof Rational) {
- return setValue((Rational) obj);
- } else if (obj instanceof Rational[]) {
- return setValue((Rational[]) obj);
- } else if (obj instanceof byte[]) {
- return setValue((byte[]) obj);
- } else if (obj instanceof Integer) {
- return setValue(((Integer) obj).intValue());
- } else if (obj instanceof Long) {
- return setValue(((Long) obj).longValue());
- } else if (obj instanceof Byte) {
- return setValue(((Byte) obj).byteValue());
- } else if (obj instanceof Short[]) {
- // Nulls in this array are treated as zeroes.
- Short[] arr = (Short[]) obj;
- int[] fin = new int[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].shortValue() & 0x0ffff;
- }
- return setValue(fin);
- } else if (obj instanceof Integer[]) {
- // Nulls in this array are treated as zeroes.
- Integer[] arr = (Integer[]) obj;
- int[] fin = new int[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].intValue();
- }
- return setValue(fin);
- } else if (obj instanceof Long[]) {
- // Nulls in this array are treated as zeroes.
- Long[] arr = (Long[]) obj;
- long[] fin = new long[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].longValue();
- }
- return setValue(fin);
- } else if (obj instanceof Byte[]) {
- // Nulls in this array are treated as zeroes.
- Byte[] arr = (Byte[]) obj;
- byte[] fin = new byte[arr.length];
- for (int i = 0; i < arr.length; i++) {
- fin[i] = (arr[i] == null) ? 0 : arr[i].byteValue();
- }
- return setValue(fin);
- } else {
- return false;
- }
- }
-
- /**
- * Sets a timestamp to this tag. The method converts the timestamp with the
- * format of "yyyy:MM:dd kk:mm:ss" and calls {@link #setValue(String)}. This
- * method will fail if the data type is not {@link #TYPE_ASCII} or the
- * component count of this tag is not 20 or undefined.
- *
- * @param time the number of milliseconds since Jan. 1, 1970 GMT
- * @return true on success
- */
- public boolean setTimeValue(long time) {
- // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
- synchronized (TIME_FORMAT) {
- return setValue(TIME_FORMAT.format(new Date(time)));
- }
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * {@link #TYPE_ASCII}.
- *
- * @return the value as a String, or null if the tag's value does not exist
- * or cannot be converted to a String.
- */
- public String getValueAsString() {
- if (mValue == null) {
- return null;
- } else if (mValue instanceof String) {
- return (String) mValue;
- } else if (mValue instanceof byte[]) {
- return new String((byte[]) mValue, US_ASCII);
- }
- return null;
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * {@link #TYPE_ASCII}.
- *
- * @param defaultValue the String to return if the tag's value does not
- * exist or cannot be converted to a String.
- * @return the tag's value as a String, or the defaultValue.
- */
- public String getValueAsString(String defaultValue) {
- String s = getValueAsString();
- if (s == null) {
- return defaultValue;
- }
- return s;
- }
-
- /**
- * Gets the value as a byte array. This method should be used for tags of
- * type {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- *
- * @return the value as a byte array, or null if the tag's value does not
- * exist or cannot be converted to a byte array.
- */
- public byte[] getValueAsBytes() {
- if (mValue instanceof byte[]) {
- return (byte[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value as a byte. If there are more than 1 bytes in this value,
- * gets the first byte. This method should be used for tags of type
- * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- *
- * @param defaultValue the byte to return if tag's value does not exist or
- * cannot be converted to a byte.
- * @return the tag's value as a byte, or the defaultValue.
- */
- public byte getValueAsByte(byte defaultValue) {
- byte[] b = getValueAsBytes();
- if (b == null || b.length < 1) {
- return defaultValue;
- }
- return b[0];
- }
-
- /**
- * Gets the value as an array of Rationals. This method should be used for
- * tags of type {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @return the value as as an array of Rationals, or null if the tag's value
- * does not exist or cannot be converted to an array of Rationals.
- */
- public Rational[] getValueAsRationals() {
- if (mValue instanceof Rational[]) {
- return (Rational[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @param defaultValue the Rational to return if tag's value does not exist
- * or cannot be converted to a Rational.
- * @return the tag's value as a Rational, or the defaultValue.
- */
- public Rational getValueAsRational(Rational defaultValue) {
- Rational[] r = getValueAsRationals();
- if (r == null || r.length < 1) {
- return defaultValue;
- }
- return r[0];
- }
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- *
- * @param defaultValue the numerator of the Rational to return if tag's
- * value does not exist or cannot be converted to a Rational (the
- * denominator will be 1).
- * @return the tag's value as a Rational, or the defaultValue.
- */
- public Rational getValueAsRational(long defaultValue) {
- Rational defaultVal = new Rational(defaultValue, 1);
- return getValueAsRational(defaultVal);
- }
-
- /**
- * Gets the value as an array of ints. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
- *
- * @return the value as as an array of ints, or null if the tag's value does
- * not exist or cannot be converted to an array of ints.
- */
- public int[] getValueAsInts() {
- if (mValue == null) {
- return null;
- } else if (mValue instanceof long[]) {
- long[] val = (long[]) mValue;
- int[] arr = new int[val.length];
- for (int i = 0; i < val.length; i++) {
- arr[i] = (int) val[i]; // Truncates
- }
- return arr;
- }
- return null;
- }
-
- /**
- * Gets the value as an int. If there are more than 1 ints in this value,
- * gets the first one. This method should be used for tags of type
- * {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
- *
- * @param defaultValue the int to return if tag's value does not exist or
- * cannot be converted to an int.
- * @return the tag's value as a int, or the defaultValue.
- */
- public int getValueAsInt(int defaultValue) {
- int[] i = getValueAsInts();
- if (i == null || i.length < 1) {
- return defaultValue;
- }
- return i[0];
- }
-
- /**
- * Gets the value as an array of longs. This method should be used for tags
- * of type {@link #TYPE_UNSIGNED_LONG}.
- *
- * @return the value as as an array of longs, or null if the tag's value
- * does not exist or cannot be converted to an array of longs.
- */
- public long[] getValueAsLongs() {
- if (mValue instanceof long[]) {
- return (long[]) mValue;
- }
- return null;
- }
-
- /**
- * Gets the value or null if none exists. If there are more than 1 longs in
- * this value, gets the first one. This method should be used for tags of
- * type {@link #TYPE_UNSIGNED_LONG}.
- *
- * @param defaultValue the long to return if tag's value does not exist or
- * cannot be converted to a long.
- * @return the tag's value as a long, or the defaultValue.
- */
- public long getValueAsLong(long defaultValue) {
- long[] l = getValueAsLongs();
- if (l == null || l.length < 1) {
- return defaultValue;
- }
- return l[0];
- }
-
- /**
- * Gets the tag's value or null if none exists.
- */
- public Object getValue() {
- return mValue;
- }
-
- /**
- * Gets a long representation of the value.
- *
- * @param defaultValue value to return if there is no value or value is a
- * rational with a denominator of 0.
- * @return the tag's value as a long, or defaultValue if no representation
- * exists.
- */
- public long forceGetValueAsLong(long defaultValue) {
- long[] l = getValueAsLongs();
- if (l != null && l.length >= 1) {
- return l[0];
- }
- byte[] b = getValueAsBytes();
- if (b != null && b.length >= 1) {
- return b[0];
- }
- Rational[] r = getValueAsRationals();
- if (r != null && r.length >= 1 && r[0].getDenominator() != 0) {
- return (long) r[0].toDouble();
- }
- return defaultValue;
- }
-
- /**
- * Gets a string representation of the value.
- */
- public String forceGetValueAsString() {
- if (mValue == null) {
- return "";
- } else if (mValue instanceof byte[]) {
- if (mDataType == TYPE_ASCII) {
- return new String((byte[]) mValue, US_ASCII);
- } else {
- return Arrays.toString((byte[]) mValue);
- }
- } else if (mValue instanceof long[]) {
- if (((long[]) mValue).length == 1) {
- return String.valueOf(((long[]) mValue)[0]);
- } else {
- return Arrays.toString((long[]) mValue);
- }
- } else if (mValue instanceof Object[]) {
- if (((Object[]) mValue).length == 1) {
- Object val = ((Object[]) mValue)[0];
- if (val == null) {
- return "";
- } else {
- return val.toString();
- }
- } else {
- return Arrays.toString((Object[]) mValue);
- }
- } else {
- return mValue.toString();
- }
- }
-
- /**
- * Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG},
- * {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE},
- * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}. For
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, call
- * {@link #getRational(int)} instead.
- *
- * @exception IllegalArgumentException if the data type is
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- */
- protected long getValueAt(int index) {
- if (mValue instanceof long[]) {
- return ((long[]) mValue)[index];
- } else if (mValue instanceof byte[]) {
- return ((byte[]) mValue)[index];
- }
- throw new IllegalArgumentException("Cannot get integer value from "
- + convertTypeToString(mDataType));
- }
-
- /**
- * Gets the {@link #TYPE_ASCII} data.
- *
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_ASCII}.
- */
- protected String getString() {
- if (mDataType != TYPE_ASCII) {
- throw new IllegalArgumentException("Cannot get ASCII value from "
- + convertTypeToString(mDataType));
- }
- return new String((byte[]) mValue, US_ASCII);
- }
-
- /*
- * Get the converted ascii byte. Used by ExifOutputStream.
- */
- protected byte[] getStringByte() {
- return (byte[]) mValue;
- }
-
- /**
- * Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data.
- *
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
- */
- protected Rational getRational(int index) {
- if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) {
- throw new IllegalArgumentException("Cannot get RATIONAL value from "
- + convertTypeToString(mDataType));
- }
- return ((Rational[]) mValue)[index];
- }
-
- /**
- * Equivalent to getBytes(buffer, 0, buffer.length).
- */
- protected void getBytes(byte[] buf) {
- getBytes(buf, 0, buf.length);
- }
-
- /**
- * Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data.
- *
- * @param buf the byte array in which to store the bytes read.
- * @param offset the initial position in buffer to store the bytes.
- * @param length the maximum number of bytes to store in buffer. If length >
- * component count, only the valid bytes will be stored.
- * @exception IllegalArgumentException If the type is NOT
- * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
- */
- protected void getBytes(byte[] buf, int offset, int length) {
- if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) {
- throw new IllegalArgumentException("Cannot get BYTE value from "
- + convertTypeToString(mDataType));
- }
- System.arraycopy(mValue, 0, buf, offset,
- (length > mComponentCountActual) ? mComponentCountActual : length);
- }
-
- /**
- * Gets the offset of this tag. This is only valid if this data size > 4 and
- * contains an offset to the location of the actual value.
- */
- protected int getOffset() {
- return mOffset;
- }
-
- /**
- * Sets the offset of this tag.
- */
- protected void setOffset(int offset) {
- mOffset = offset;
- }
-
- protected void setHasDefinedCount(boolean d) {
- mHasDefinedDefaultComponentCount = d;
- }
-
- protected boolean hasDefinedCount() {
- return mHasDefinedDefaultComponentCount;
- }
-
- private boolean checkBadComponentCount(int count) {
- if (mHasDefinedDefaultComponentCount && (mComponentCountActual != count)) {
- return true;
- }
- return false;
- }
-
- private static String convertTypeToString(short type) {
- switch (type) {
- case TYPE_UNSIGNED_BYTE:
- return "UNSIGNED_BYTE";
- case TYPE_ASCII:
- return "ASCII";
- case TYPE_UNSIGNED_SHORT:
- return "UNSIGNED_SHORT";
- case TYPE_UNSIGNED_LONG:
- return "UNSIGNED_LONG";
- case TYPE_UNSIGNED_RATIONAL:
- return "UNSIGNED_RATIONAL";
- case TYPE_UNDEFINED:
- return "UNDEFINED";
- case TYPE_LONG:
- return "LONG";
- case TYPE_RATIONAL:
- return "RATIONAL";
- default:
- return "";
- }
- }
-
- private boolean checkOverflowForUnsignedShort(int[] value) {
- for (int v : value) {
- if (v > UNSIGNED_SHORT_MAX || v < 0) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedLong(long[] value) {
- for (long v : value) {
- if (v < 0 || v > UNSIGNED_LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedLong(int[] value) {
- for (int v : value) {
- if (v < 0) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForUnsignedRational(Rational[] value) {
- for (Rational v : value) {
- if (v.getNumerator() < 0 || v.getDenominator() < 0
- || v.getNumerator() > UNSIGNED_LONG_MAX
- || v.getDenominator() > UNSIGNED_LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkOverflowForRational(Rational[] value) {
- for (Rational v : value) {
- if (v.getNumerator() < LONG_MIN || v.getDenominator() < LONG_MIN
- || v.getNumerator() > LONG_MAX
- || v.getDenominator() > LONG_MAX) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj instanceof ExifTag) {
- ExifTag tag = (ExifTag) obj;
- if (tag.mTagId != this.mTagId
- || tag.mComponentCountActual != this.mComponentCountActual
- || tag.mDataType != this.mDataType) {
- return false;
- }
- if (mValue != null) {
- if (tag.mValue == null) {
- return false;
- } else if (mValue instanceof long[]) {
- if (!(tag.mValue instanceof long[])) {
- return false;
- }
- return Arrays.equals((long[]) mValue, (long[]) tag.mValue);
- } else if (mValue instanceof Rational[]) {
- if (!(tag.mValue instanceof Rational[])) {
- return false;
- }
- return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue);
- } else if (mValue instanceof byte[]) {
- if (!(tag.mValue instanceof byte[])) {
- return false;
- }
- return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue);
- } else {
- return mValue.equals(tag.mValue);
- }
- } else {
- return tag.mValue == null;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return String.format("tag id: %04X\n", mTagId) + "ifd id: " + mIfd + "\ntype: "
- + convertTypeToString(mDataType) + "\ncount: " + mComponentCountActual
- + "\noffset: " + mOffset + "\nvalue: " + forceGetValueAsString() + "\n";
- }
-
-}
diff --git a/src/com/android/camera/exif/IfdData.java b/src/com/android/camera/exif/IfdData.java
deleted file mode 100644
index 132a8ebc5..000000000
--- a/src/com/android/camera/exif/IfdData.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This class stores all the tags in an IFD.
- *
- * @see ExifData
- * @see ExifTag
- */
-class IfdData {
-
- private final int mIfdId;
- private final Map<Short, ExifTag> mExifTags = new HashMap<Short, ExifTag>();
- private int mOffsetToNextIfd = 0;
- private static final int[] sIfds = {
- IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1, IfdId.TYPE_IFD_EXIF,
- IfdId.TYPE_IFD_INTEROPERABILITY, IfdId.TYPE_IFD_GPS
- };
- /**
- * Creates an IfdData with given IFD ID.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- IfdData(int ifdId) {
- mIfdId = ifdId;
- }
-
- static protected int[] getIfds() {
- return sIfds;
- }
-
- /**
- * Get a array the contains all {@link ExifTag} in this IFD.
- */
- protected ExifTag[] getAllTags() {
- return mExifTags.values().toArray(new ExifTag[mExifTags.size()]);
- }
-
- /**
- * Gets the ID of this IFD.
- *
- * @see IfdId#TYPE_IFD_0
- * @see IfdId#TYPE_IFD_1
- * @see IfdId#TYPE_IFD_EXIF
- * @see IfdId#TYPE_IFD_GPS
- * @see IfdId#TYPE_IFD_INTEROPERABILITY
- */
- protected int getId() {
- return mIfdId;
- }
-
- /**
- * Gets the {@link ExifTag} with given tag id. Return null if there is no
- * such tag.
- */
- protected ExifTag getTag(short tagId) {
- return mExifTags.get(tagId);
- }
-
- /**
- * Adds or replaces a {@link ExifTag}.
- */
- protected ExifTag setTag(ExifTag tag) {
- tag.setIfd(mIfdId);
- return mExifTags.put(tag.getTagId(), tag);
- }
-
- protected boolean checkCollision(short tagId) {
- return mExifTags.get(tagId) != null;
- }
-
- /**
- * Removes the tag of the given ID
- */
- protected void removeTag(short tagId) {
- mExifTags.remove(tagId);
- }
-
- /**
- * Gets the tags count in the IFD.
- */
- protected int getTagCount() {
- return mExifTags.size();
- }
-
- /**
- * Sets the offset of next IFD.
- */
- protected void setOffsetToNextIfd(int offset) {
- mOffsetToNextIfd = offset;
- }
-
- /**
- * Gets the offset of next IFD.
- */
- protected int getOffsetToNextIfd() {
- return mOffsetToNextIfd;
- }
-
- /**
- * Returns true if all tags in this two IFDs are equal. Note that tags of
- * IFDs offset or thumbnail offset will be ignored.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (obj instanceof IfdData) {
- IfdData data = (IfdData) obj;
- if (data.getId() == mIfdId && data.getTagCount() == getTagCount()) {
- ExifTag[] tags = data.getAllTags();
- for (ExifTag tag : tags) {
- if (ExifInterface.isOffsetTag(tag.getTagId())) {
- continue;
- }
- ExifTag tag2 = mExifTags.get(tag.getTagId());
- if (!tag.equals(tag2)) {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/camera/exif/IfdId.java b/src/com/android/camera/exif/IfdId.java
deleted file mode 100644
index 9fec68874..000000000
--- a/src/com/android/camera/exif/IfdId.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-/**
- * The constants of the IFD ID defined in EXIF spec.
- */
-public interface IfdId {
- public static final int TYPE_IFD_0 = 0;
- public static final int TYPE_IFD_1 = 1;
- public static final int TYPE_IFD_EXIF = 2;
- public static final int TYPE_IFD_INTEROPERABILITY = 3;
- public static final int TYPE_IFD_GPS = 4;
- /* This is used in ExifData to allocate enough IfdData */
- static final int TYPE_IFD_COUNT = 5;
-
-}
diff --git a/src/com/android/camera/exif/JpegHeader.java b/src/com/android/camera/exif/JpegHeader.java
deleted file mode 100644
index 383617af4..000000000
--- a/src/com/android/camera/exif/JpegHeader.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-class JpegHeader {
- public static final short SOI = (short) 0xFFD8;
- public static final short APP1 = (short) 0xFFE1;
- public static final short APP0 = (short) 0xFFE0;
- public static final short EOI = (short) 0xFFD9;
-
- /**
- * SOF (start of frame). All value between SOF0 and SOF15 is SOF marker except for DHT, JPG,
- * and DAC marker.
- */
- public static final short SOF0 = (short) 0xFFC0;
- public static final short SOF15 = (short) 0xFFCF;
- public static final short DHT = (short) 0xFFC4;
- public static final short JPG = (short) 0xFFC8;
- public static final short DAC = (short) 0xFFCC;
-
- public static final boolean isSofMarker(short marker) {
- return marker >= SOF0 && marker <= SOF15 && marker != DHT && marker != JPG
- && marker != DAC;
- }
-}
diff --git a/src/com/android/camera/exif/OrderedDataOutputStream.java b/src/com/android/camera/exif/OrderedDataOutputStream.java
deleted file mode 100644
index 1a1b31be4..000000000
--- a/src/com/android/camera/exif/OrderedDataOutputStream.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-class OrderedDataOutputStream extends FilterOutputStream {
- private final ByteBuffer mByteBuffer = ByteBuffer.allocate(4);
-
- public OrderedDataOutputStream(OutputStream out) {
- super(out);
- }
-
- public OrderedDataOutputStream setByteOrder(ByteOrder order) {
- mByteBuffer.order(order);
- return this;
- }
-
- public OrderedDataOutputStream writeShort(short value) throws IOException {
- mByteBuffer.rewind();
- mByteBuffer.putShort(value);
- out.write(mByteBuffer.array(), 0, 2);
- return this;
- }
-
- public OrderedDataOutputStream writeInt(int value) throws IOException {
- mByteBuffer.rewind();
- mByteBuffer.putInt(value);
- out.write(mByteBuffer.array());
- return this;
- }
-
- public OrderedDataOutputStream writeRational(Rational rational) throws IOException {
- writeInt((int) rational.getNumerator());
- writeInt((int) rational.getDenominator());
- return this;
- }
-}
diff --git a/src/com/android/camera/exif/Rational.java b/src/com/android/camera/exif/Rational.java
deleted file mode 100644
index 96b5312b0..000000000
--- a/src/com/android/camera/exif/Rational.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera.exif;
-
-/**
- * The rational data type of EXIF tag. Contains a pair of longs representing the
- * numerator and denominator of a Rational number.
- */
-public class Rational {
-
- private final long mNumerator;
- private final long mDenominator;
-
- /**
- * Create a Rational with a given numerator and denominator.
- *
- * @param nominator
- * @param denominator
- */
- public Rational(long nominator, long denominator) {
- mNumerator = nominator;
- mDenominator = denominator;
- }
-
- /**
- * Create a copy of a Rational.
- */
- public Rational(Rational r) {
- mNumerator = r.mNumerator;
- mDenominator = r.mDenominator;
- }
-
- /**
- * Gets the numerator of the rational.
- */
- public long getNumerator() {
- return mNumerator;
- }
-
- /**
- * Gets the denominator of the rational
- */
- public long getDenominator() {
- return mDenominator;
- }
-
- /**
- * Gets the rational value as type double. Will cause a divide-by-zero error
- * if the denominator is 0.
- */
- public double toDouble() {
- return mNumerator / (double) mDenominator;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (obj instanceof Rational) {
- Rational data = (Rational) obj;
- return mNumerator == data.mNumerator && mDenominator == data.mDenominator;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mNumerator + "/" + mDenominator;
- }
-}
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java
index 792fb4376..85ebd6cc5 100644
--- a/src/com/android/camera/ui/FilmStripView.java
+++ b/src/com/android/camera/ui/FilmStripView.java
@@ -862,8 +862,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
}
int currentViewCenter = currentItem.getCenterX();
if (mCenterX != currentViewCenter) {
- int snapInTime = SNAP_IN_CENTER_TIME_MS
- * Math.abs(mCenterX - currentViewCenter) / mDrawArea.width();
+ int snapInTime = (int) (SNAP_IN_CENTER_TIME_MS
+ * Math.abs(mCenterX - currentViewCenter) / mDrawArea.width());
mController.scrollToPosition(currentViewCenter,
snapInTime, false);
}