am b6d09a72: am ceb0b557: Merge "Add EXIF rotation support to wallpaper picker" into jb-ub-now-indigo-rose

* commit 'b6d09a72acfbdbf892bfda3b232f0b3276692b7f':
  Add EXIF rotation support to wallpaper picker
This commit is contained in:
Adam Cohen
2013-10-12 16:39:30 -07:00
committed by Android Git Automerger
5 changed files with 262 additions and 73 deletions
+89 -16
View File
@@ -17,9 +17,11 @@
package com.android.launcher3; package com.android.launcher3;
import android.content.Context; import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.RectF; import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.ScaleGestureDetector.OnScaleGestureListener;
@@ -36,10 +38,18 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
private long mTouchDownTime; private long mTouchDownTime;
private float mFirstX, mFirstY; private float mFirstX, mFirstY;
private float mLastX, mLastY; private float mLastX, mLastY;
private float mCenterX, mCenterY;
private float mMinScale; private float mMinScale;
private boolean mTouchEnabled = true; private boolean mTouchEnabled = true;
private RectF mTempEdges = new RectF(); private RectF mTempEdges = new RectF();
private float[] mTempPoint = new float[] { 0, 0 };
private float[] mTempCoef = new float[] { 0, 0 };
private float[] mTempAdjustment = new float[] { 0, 0 };
private float[] mTempImageDims = new float[] { 0, 0 };
private float[] mTempRendererCenter = new float[] { 0, 0 };
TouchCallback mTouchCallback; TouchCallback mTouchCallback;
Matrix mRotateMatrix;
Matrix mInverseRotateMatrix;
public interface TouchCallback { public interface TouchCallback {
void onTouchDown(); void onTouchDown();
@@ -54,17 +64,43 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
public CropView(Context context, AttributeSet attrs) { public CropView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mScaleGestureDetector = new ScaleGestureDetector(context, this); mScaleGestureDetector = new ScaleGestureDetector(context, this);
mRotateMatrix = new Matrix();
mInverseRotateMatrix = new Matrix();
}
private float[] getImageDims() {
final float imageWidth = mRenderer.source.getImageWidth();
final float imageHeight = mRenderer.source.getImageHeight();
float[] imageDims = mTempImageDims;
imageDims[0] = imageWidth;
imageDims[1] = imageHeight;
mRotateMatrix.mapPoints(imageDims);
imageDims[0] = Math.abs(imageDims[0]);
imageDims[1] = Math.abs(imageDims[1]);
return imageDims;
} }
private void getEdgesHelper(RectF edgesOut) { private void getEdgesHelper(RectF edgesOut) {
final float width = getWidth(); final float width = getWidth();
final float height = getHeight(); final float height = getHeight();
final float imageWidth = mRenderer.source.getImageWidth(); final float[] imageDims = getImageDims();
final float imageHeight = mRenderer.source.getImageHeight(); final float imageWidth = imageDims[0];
final float imageHeight = imageDims[1];
float initialCenterX = mRenderer.source.getImageWidth() / 2f;
float initialCenterY = mRenderer.source.getImageHeight() / 2f;
float[] rendererCenter = mTempRendererCenter;
rendererCenter[0] = mCenterX - initialCenterX;
rendererCenter[1] = mCenterY - initialCenterY;
mRotateMatrix.mapPoints(rendererCenter);
rendererCenter[0] += imageWidth / 2;
rendererCenter[1] += imageHeight / 2;
final float scale = mRenderer.scale; final float scale = mRenderer.scale;
float centerX = (width / 2f - mRenderer.centerX + (imageWidth - width) / 2f) float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f)
* scale + width / 2f; * scale + width / 2f;
float centerY = (height / 2f - mRenderer.centerY + (imageHeight - height) / 2f) float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f)
* scale + height / 2f; * scale + height / 2f;
float leftEdge = centerX - imageWidth / 2f * scale; float leftEdge = centerX - imageWidth / 2f * scale;
float rightEdge = centerX + imageWidth / 2f * scale; float rightEdge = centerX + imageWidth / 2f * scale;
@@ -77,6 +113,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
edgesOut.bottom = bottomEdge; edgesOut.bottom = bottomEdge;
} }
public int getImageRotation() {
return mRenderer.rotation;
}
public RectF getCrop() { public RectF getCrop() {
final RectF edges = mTempEdges; final RectF edges = mTempEdges;
getEdgesHelper(edges); getEdgesHelper(edges);
@@ -96,6 +136,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
public void setTileSource(TileSource source, Runnable isReadyCallback) { public void setTileSource(TileSource source, Runnable isReadyCallback) {
super.setTileSource(source, isReadyCallback); super.setTileSource(source, isReadyCallback);
mCenterX = mRenderer.centerX;
mCenterY = mRenderer.centerY;
mRotateMatrix.reset();
mRotateMatrix.setRotate(mRenderer.rotation);
mInverseRotateMatrix.reset();
mInverseRotateMatrix.setRotate(-mRenderer.rotation);
updateMinScale(getWidth(), getHeight(), source, true); updateMinScale(getWidth(), getHeight(), source, true);
} }
@@ -115,8 +161,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
mRenderer.scale = 1; mRenderer.scale = 1;
} }
if (source != null) { if (source != null) {
mMinScale = Math.max(w / (float) source.getImageWidth(), final float[] imageDims = getImageDims();
h / (float) source.getImageHeight()); final float imageWidth = imageDims[0];
final float imageHeight = imageDims[1];
mMinScale = Math.max(w / imageWidth, h / imageHeight);
mRenderer.scale = Math.max(mMinScale, mRenderer.scale); mRenderer.scale = Math.max(mMinScale, mRenderer.scale);
} }
} }
@@ -154,7 +202,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
final RectF edges = mTempEdges; final RectF edges = mTempEdges;
getEdgesHelper(edges); getEdgesHelper(edges);
final float scale = mRenderer.scale; final float scale = mRenderer.scale;
mRenderer.centerX += Math.ceil(edges.left / scale); mCenterX += Math.ceil(edges.left / scale);
updateCenter();
}
private void updateCenter() {
mRenderer.centerX = Math.round(mCenterX);
mRenderer.centerY = Math.round(mCenterY);
} }
public void setTouchEnabled(boolean enabled) { public void setTouchEnabled(boolean enabled) {
@@ -215,8 +269,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
mScaleGestureDetector.onTouchEvent(event); mScaleGestureDetector.onTouchEvent(event);
switch (action) { switch (action) {
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
mRenderer.centerX += (mLastX - x) / mRenderer.scale; float[] point = mTempPoint;
mRenderer.centerY += (mLastY - y) / mRenderer.scale; point[0] = (mLastX - x) / mRenderer.scale;
point[1] = (mLastY - y) / mRenderer.scale;
mInverseRotateMatrix.mapPoints(point);
mCenterX += point[0];
mCenterY += point[1];
updateCenter();
invalidate(); invalidate();
break; break;
} }
@@ -226,18 +285,32 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
final RectF edges = mTempEdges; final RectF edges = mTempEdges;
getEdgesHelper(edges); getEdgesHelper(edges);
final float scale = mRenderer.scale; final float scale = mRenderer.scale;
float[] coef = mTempCoef;
coef[0] = 1;
coef[1] = 1;
mRotateMatrix.mapPoints(coef);
float[] adjustment = mTempAdjustment;
mTempAdjustment[0] = 0;
mTempAdjustment[1] = 0;
if (edges.left > 0) { if (edges.left > 0) {
mRenderer.centerX += Math.ceil(edges.left / scale); adjustment[0] = edges.left / scale;
} } else if (edges.right < getWidth()) {
if (edges.right < getWidth()) { adjustment[0] = (edges.right - getWidth()) / scale;
mRenderer.centerX += (edges.right - getWidth()) / scale;
} }
if (edges.top > 0) { if (edges.top > 0) {
mRenderer.centerY += Math.ceil(edges.top / scale); adjustment[1] = FloatMath.ceil(edges.top / scale);
} else if (edges.bottom < getHeight()) {
adjustment[1] = (edges.bottom - getHeight()) / scale;
} }
if (edges.bottom < getHeight()) { for (int dim = 0; dim <= 1; dim++) {
mRenderer.centerY += (edges.bottom - getHeight()) / scale; if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]);
} }
mInverseRotateMatrix.mapPoints(adjustment);
mCenterX += adjustment[0];
mCenterY += adjustment[1];
updateCenter();
} }
} }
@@ -61,7 +61,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
String imageFilename = a.getSavedImages().getImageFilename(mDbId); String imageFilename = a.getSavedImages().getImageFilename(mDbId);
File file = new File(a.getFilesDir(), imageFilename); File file = new File(a.getFilesDir(), imageFilename);
CropView v = a.getCropView(); CropView v = a.getCropView();
v.setTileSource(new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, 0), null); int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath());
v.setTileSource(
new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null);
v.moveToLeft(); v.moveToLeft();
v.setTouchEnabled(false); v.setTouchEnabled(false);
} }
@@ -37,12 +37,14 @@ import android.graphics.RectF;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import com.android.gallery3d.common.Utils; import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@@ -88,7 +90,8 @@ public class WallpaperCropActivity extends Activity {
Intent cropIntent = this.getIntent(); Intent cropIntent = this.getIntent();
final Uri imageUri = cropIntent.getData(); final Uri imageUri = cropIntent.getData();
mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, 0), null); int rotation = getRotationFromExif(this, imageUri);
mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
mCropView.setTouchEnabled(true); mCropView.setTouchEnabled(true);
// Action bar // Action bar
// Show the custom action bar view // Show the custom action bar view
@@ -168,9 +171,47 @@ public class WallpaperCropActivity extends Activity {
return new Point(defaultWidth, defaultHeight); return new Point(defaultWidth, defaultHeight);
} }
public static int getRotationFromExif(String path) {
return getRotationFromExifHelper(path, null, 0, null, null);
}
public static int getRotationFromExif(Context context, Uri uri) {
return getRotationFromExifHelper(null, null, 0, context, uri);
}
public static int getRotationFromExif(Resources res, int resId) {
return getRotationFromExifHelper(null, res, resId, null, null);
}
private static int getRotationFromExifHelper(
String path, Resources res, int resId, Context context, Uri uri) {
ExifInterface ei = new ExifInterface();
try {
if (path != null) {
ei.readExif(path);
} else if (uri != null) {
InputStream is = context.getContentResolver().openInputStream(uri);
BufferedInputStream bis = new BufferedInputStream(is);
ei.readExif(bis);
} else {
InputStream is = res.openRawResource(resId);
BufferedInputStream bis = new BufferedInputStream(is);
ei.readExif(bis);
}
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
if (ori != null) {
return ExifInterface.getRotationForOrientationValue(ori.shortValue());
}
} catch (IOException e) {
Log.w(LOGTAG, "Getting exif data failed", e);
}
return 0;
}
protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) { protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
BitmapCropTask cropTask = new BitmapCropTask(this, int rotation = getRotationFromExif(filePath);
filePath, null, 0, 0, true, false, null); BitmapCropTask cropTask = new BitmapCropTask(
this, filePath, null, rotation, 0, 0, true, false, null);
final Point bounds = cropTask.getImageBounds(); final Point bounds = cropTask.getImageBounds();
Runnable onEndCrop = new Runnable() { Runnable onEndCrop = new Runnable() {
public void run() { public void run() {
@@ -190,6 +231,7 @@ public class WallpaperCropActivity extends Activity {
Resources res, int resId, final boolean finishActivityWhenDone) { Resources res, int resId, final boolean finishActivityWhenDone) {
// crop this image and scale it down to the default wallpaper size for // crop this image and scale it down to the default wallpaper size for
// this device // this device
int rotation = getRotationFromExif(res, resId);
Point inSize = mCropView.getSourceDimensions(); Point inSize = mCropView.getSourceDimensions();
Point outSize = getDefaultWallpaperSize(getResources(), Point outSize = getDefaultWallpaperSize(getResources(),
getWindowManager()); getWindowManager());
@@ -207,8 +249,7 @@ public class WallpaperCropActivity extends Activity {
} }
}; };
BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
crop, outSize.x, outSize.y, crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
true, false, onEndCrop);
cropTask.execute(); cropTask.execute();
} }
@@ -220,8 +261,6 @@ public class WallpaperCropActivity extends Activity {
protected void cropImageAndSetWallpaper(Uri uri, protected void cropImageAndSetWallpaper(Uri uri,
OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
// Get the crop // Get the crop
Point inSize = mCropView.getSourceDimensions();
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
Point minDims = new Point(); Point minDims = new Point();
@@ -260,12 +299,21 @@ public class WallpaperCropActivity extends Activity {
} }
// Get the crop // Get the crop
RectF cropRect = mCropView.getCrop(); RectF cropRect = mCropView.getCrop();
int cropRotation = mCropView.getImageRotation();
float cropScale = mCropView.getWidth() / (float) cropRect.width(); float cropScale = mCropView.getWidth() / (float) cropRect.width();
Point inSize = mCropView.getSourceDimensions();
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(cropRotation);
float[] rotatedInSize = new float[] { inSize.x, inSize.y };
rotateMatrix.mapPoints(rotatedInSize);
rotatedInSize[0] = Math.abs(rotatedInSize[0]);
rotatedInSize[1] = Math.abs(rotatedInSize[1]);
// ADJUST CROP WIDTH // ADJUST CROP WIDTH
// Extend the crop all the way to the right, for parallax // Extend the crop all the way to the right, for parallax
// (or all the way to the left, in RTL) // (or all the way to the left, in RTL)
float extraSpace = ltr ? inSize.x - cropRect.right : cropRect.left; float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
// Cap the amount of extra width // Cap the amount of extra width
float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width(); float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width();
extraSpace = Math.min(extraSpace, maxExtraSpace); extraSpace = Math.min(extraSpace, maxExtraSpace);
@@ -283,7 +331,7 @@ public class WallpaperCropActivity extends Activity {
float extraPortraitHeight = float extraPortraitHeight =
portraitHeight / cropScale - cropRect.height(); portraitHeight / cropScale - cropRect.height();
float expandHeight = float expandHeight =
Math.min(Math.min(inSize.y - cropRect.bottom, cropRect.top), Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
extraPortraitHeight / 2); extraPortraitHeight / 2);
cropRect.top -= expandHeight; cropRect.top -= expandHeight;
cropRect.bottom += expandHeight; cropRect.bottom += expandHeight;
@@ -301,7 +349,7 @@ public class WallpaperCropActivity extends Activity {
} }
}; };
BitmapCropTask cropTask = new BitmapCropTask(this, uri, BitmapCropTask cropTask = new BitmapCropTask(this, uri,
cropRect, outWidth, outHeight, true, false, onEndCrop); cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
if (onBitmapCroppedHandler != null) { if (onBitmapCroppedHandler != null) {
cropTask.setOnBitmapCropped(onBitmapCroppedHandler); cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
} }
@@ -321,7 +369,7 @@ public class WallpaperCropActivity extends Activity {
InputStream mInStream; InputStream mInStream;
RectF mCropBounds = null; RectF mCropBounds = null;
int mOutWidth, mOutHeight; int mOutWidth, mOutHeight;
int mRotation = 0; // for now int mRotation;
String mOutputFormat = "jpg"; // for now String mOutputFormat = "jpg"; // for now
boolean mSetWallpaper; boolean mSetWallpaper;
boolean mSaveCroppedBitmap; boolean mSaveCroppedBitmap;
@@ -332,40 +380,45 @@ public class WallpaperCropActivity extends Activity {
boolean mNoCrop; boolean mNoCrop;
public BitmapCropTask(Context c, String filePath, public BitmapCropTask(Context c, String filePath,
RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mContext = c; mContext = c;
mInFilePath = filePath; mInFilePath = filePath;
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation,
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
} }
public BitmapCropTask(byte[] imageBytes, public BitmapCropTask(byte[] imageBytes,
RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mInImageBytes = imageBytes; mInImageBytes = imageBytes;
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation,
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
} }
public BitmapCropTask(Context c, Uri inUri, public BitmapCropTask(Context c, Uri inUri,
RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mContext = c; mContext = c;
mInUri = inUri; mInUri = inUri;
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation,
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
} }
public BitmapCropTask(Context c, Resources res, int inResId, public BitmapCropTask(Context c, Resources res, int inResId,
RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mContext = c; mContext = c;
mInResId = inResId; mInResId = inResId;
mResources = res; mResources = res;
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation,
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
} }
private void init(RectF cropBounds, int outWidth, int outHeight, private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mCropBounds = cropBounds; mCropBounds = cropBounds;
mRotation = rotation;
mOutWidth = outWidth; mOutWidth = outWidth;
mOutHeight = outHeight; mOutHeight = outHeight;
mSetWallpaper = setWallpaper; mSetWallpaper = setWallpaper;
@@ -452,6 +505,29 @@ public class WallpaperCropActivity extends Activity {
if (mInStream != null) { if (mInStream != null) {
// Find crop bounds (scaled to original image size) // Find crop bounds (scaled to original image size)
Rect roundedTrueCrop = new Rect(); Rect roundedTrueCrop = new Rect();
Matrix rotateMatrix = new Matrix();
Matrix inverseRotateMatrix = new Matrix();
if (mRotation > 0) {
rotateMatrix.setRotate(mRotation);
inverseRotateMatrix.setRotate(-mRotation);
mCropBounds.roundOut(roundedTrueCrop);
mCropBounds = new RectF(roundedTrueCrop);
Point bounds = getImageBounds();
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
rotateMatrix.mapPoints(rotatedBounds);
rotatedBounds[0] = Math.abs(rotatedBounds[0]);
rotatedBounds[1] = Math.abs(rotatedBounds[1]);
mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
inverseRotateMatrix.mapRect(mCropBounds);
mCropBounds.offset(bounds.x/2, bounds.y/2);
regenerateInputStream();
}
mCropBounds.roundOut(roundedTrueCrop); mCropBounds.roundOut(roundedTrueCrop);
if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
@@ -495,6 +571,12 @@ public class WallpaperCropActivity extends Activity {
fullSize = BitmapFactory.decodeStream(mInStream, null, options); fullSize = BitmapFactory.decodeStream(mInStream, null, options);
} }
if (fullSize != null) { if (fullSize != null) {
mCropBounds.left /= scaleDownSampleSize;
mCropBounds.top /= scaleDownSampleSize;
mCropBounds.bottom /= scaleDownSampleSize;
mCropBounds.right /= scaleDownSampleSize;
mCropBounds.roundOut(roundedTrueCrop);
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.top, roundedTrueCrop.width(),
roundedTrueCrop.height()); roundedTrueCrop.height());
@@ -506,16 +588,40 @@ public class WallpaperCropActivity extends Activity {
failure = true; failure = true;
return false; return false;
} }
if (mOutWidth > 0 && mOutHeight > 0) { if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
Matrix m = new Matrix(); float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); rotateMatrix.mapPoints(dimsAfter);
if (mRotation > 0) { dimsAfter[0] = Math.abs(dimsAfter[0]);
m.setRotate(mRotation); dimsAfter[1] = Math.abs(dimsAfter[1]);
m.mapRect(cropRect);
if (!(mOutWidth > 0 && mOutHeight > 0)) {
mOutWidth = Math.round(dimsAfter[0]);
mOutHeight = Math.round(dimsAfter[1]);
} }
RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
m.preRotate(mRotation); Matrix m = new Matrix();
if (mRotation == 0) {
m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
} else {
Matrix m1 = new Matrix();
m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
Matrix m2 = new Matrix();
m2.setRotate(mRotation);
Matrix m3 = new Matrix();
m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
Matrix m4 = new Matrix();
m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
Matrix c1 = new Matrix();
c1.setConcat(m2, m1);
Matrix c2 = new Matrix();
c2.setConcat(m4, m3);
m.setConcat(c2, c1);
}
Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
(int) returnRect.height(), Bitmap.Config.ARGB_8888); (int) returnRect.height(), Bitmap.Config.ARGB_8888);
if (tmp != null) { if (tmp != null) {
@@ -525,14 +631,6 @@ public class WallpaperCropActivity extends Activity {
c.drawBitmap(crop, m, p); c.drawBitmap(crop, m, p);
crop = tmp; crop = tmp;
} }
} else if (mRotation > 0) {
Matrix m = new Matrix();
m.setRotate(mRotation);
Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(),
crop.getHeight(), m, true);
if (tmp != null) {
crop = tmp;
}
} }
if (mSaveCroppedBitmap) { if (mSaveCroppedBitmap) {
@@ -601,8 +699,7 @@ public class WallpaperCropActivity extends Activity {
final SharedPreferences sharedPrefs, final SharedPreferences sharedPrefs,
WindowManager windowManager, WindowManager windowManager,
final WallpaperManager wallpaperManager) { final WallpaperManager wallpaperManager) {
final Point defaultWallpaperSize = final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
WallpaperCropActivity.getDefaultWallpaperSize(res, windowManager);
new Thread("suggestWallpaperDimension") { new Thread("suggestWallpaperDimension") {
public void run() { public void run() {
@@ -614,7 +711,6 @@ public class WallpaperCropActivity extends Activity {
}.start(); }.start();
} }
protected static RectF getMaxCropRect( protected static RectF getMaxCropRect(
int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
RectF cropRect = new RectF(); RectF cropRect = new RectF();
@@ -30,6 +30,7 @@ import android.database.Cursor;
import android.database.DataSetObserver; import android.database.DataSetObserver;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.Rect; import android.graphics.Rect;
@@ -61,11 +62,14 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import com.android.gallery3d.exif.ExifInterface;
import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
public class WallpaperPickerActivity extends WallpaperCropActivity { public class WallpaperPickerActivity extends WallpaperCropActivity {
@@ -126,8 +130,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
@Override @Override
public void onClick(WallpaperPickerActivity a) { public void onClick(WallpaperPickerActivity a) {
CropView v = a.getCropView(); CropView v = a.getCropView();
v.setTileSource(new BitmapRegionTileSource( int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
a, mUri, 1024, 0), null); v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
v.setTouchEnabled(true); v.setTouchEnabled(true);
} }
@Override @Override
@@ -136,8 +140,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() { OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
public void onBitmapCropped(byte[] imageBytes) { public void onBitmapCropped(byte[] imageBytes) {
Point thumbSize = getDefaultThumbnailSize(a.getResources()); Point thumbSize = getDefaultThumbnailSize(a.getResources());
Bitmap thumb = // rotation is set to 0 since imageBytes has already been correctly rotated
createThumbnail(thumbSize, null, null, imageBytes, null, 0, true); Bitmap thumb = createThumbnail(
thumbSize, null, null, imageBytes, null, 0, 0, true);
a.getSavedImages().writeImage(thumb, imageBytes); a.getSavedImages().writeImage(thumb, imageBytes);
} }
}; };
@@ -165,8 +170,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
} }
@Override @Override
public void onClick(WallpaperPickerActivity a) { public void onClick(WallpaperPickerActivity a) {
int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
BitmapRegionTileSource source = new BitmapRegionTileSource( BitmapRegionTileSource source = new BitmapRegionTileSource(
mResources, a, mResId, 1024, 0); mResources, a, mResId, 1024, rotation);
CropView v = a.getCropView(); CropView v = a.getCropView();
v.setTileSource(source, null); v.setTileSource(source, null);
Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize( Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(
@@ -199,7 +205,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
// called by onCreate; this is subclassed to overwrite WallpaperCropActivity // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
protected void init() { protected void init() {
setContentView(R.layout.wallpaper_picker); setContentView(R.layout.wallpaper_picker);
final WallpaperRootView root = (WallpaperRootView) findViewById(R.id.wallpaper_root);
mCropView = (CropView) findViewById(R.id.cropView); mCropView = (CropView) findViewById(R.id.cropView);
mWallpaperStrip = findViewById(R.id.wallpaper_strip); mWallpaperStrip = findViewById(R.id.wallpaper_strip);
@@ -522,7 +527,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
private void updateTileIndices() { private void updateTileIndices() {
LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
final int childCount = masterWallpaperList.getChildCount(); final int childCount = masterWallpaperList.getChildCount();
ArrayList<WallpaperTileInfo> tiles = new ArrayList<WallpaperTileInfo>();
final Resources res = getResources(); final Resources res = getResources();
// Do two passes; the first pass gets the total number of tiles // Do two passes; the first pass gets the total number of tiles
@@ -568,26 +572,35 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
} }
private static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, private static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
Resources res, int resId, boolean leftAligned) { Resources res, int resId, int rotation, boolean leftAligned) {
int width = size.x; int width = size.x;
int height = size.y; int height = size.y;
BitmapCropTask cropTask; BitmapCropTask cropTask;
if (uri != null) { if (uri != null) {
cropTask = new BitmapCropTask(context, uri, null, width, height, false, true, null); cropTask = new BitmapCropTask(
context, uri, null, rotation, width, height, false, true, null);
} else if (imageBytes != null) { } else if (imageBytes != null) {
cropTask = new BitmapCropTask(imageBytes, null, width, height, false, true, null); cropTask = new BitmapCropTask(
imageBytes, null, rotation, width, height, false, true, null);
} else { } else {
cropTask = cropTask = new BitmapCropTask(
new BitmapCropTask(context, res, resId, null, width, height, false, true, null); context, res, resId, null, rotation, width, height, false, true, null);
} }
Point bounds = cropTask.getImageBounds(); Point bounds = cropTask.getImageBounds();
if (bounds == null || bounds.x == 0 || bounds.y == 0) { if (bounds == null || bounds.x == 0 || bounds.y == 0) {
return null; return null;
} }
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(rotation);
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
rotateMatrix.mapPoints(rotatedBounds);
rotatedBounds[0] = Math.abs(rotatedBounds[0]);
rotatedBounds[1] = Math.abs(rotatedBounds[1]);
RectF cropRect = WallpaperCropActivity.getMaxCropRect( RectF cropRect = WallpaperCropActivity.getMaxCropRect(
bounds.x, bounds.y, width, height, leftAligned); (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
cropTask.setCropBounds(cropRect); cropTask.setCropBounds(cropRect);
if (cropTask.cropBitmap()) { if (cropTask.cropBitmap()) {
@@ -607,7 +620,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
// Load the thumbnail // Load the thumbnail
ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
Point defaultSize = getDefaultThumbnailSize(this.getResources()); Point defaultSize = getDefaultThumbnailSize(this.getResources());
Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, false); int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
if (thumb != null) { if (thumb != null) {
image.setImageBitmap(thumb); image.setImageBitmap(thumb);
Drawable thumbDrawable = image.getDrawable(); Drawable thumbDrawable = image.getDrawable();
@@ -620,6 +634,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
UriWallpaperInfo info = new UriWallpaperInfo(uri); UriWallpaperInfo info = new UriWallpaperInfo(uri);
pickedImageThumbnail.setTag(info); pickedImageThumbnail.setTag(info);
info.setView(pickedImageThumbnail); info.setView(pickedImageThumbnail);
addLongPressHandler(pickedImageThumbnail);
updateTileIndices(); updateTileIndices();
pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener); pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
mThumbnailOnClickListener.onClick(pickedImageThumbnail); mThumbnailOnClickListener.onClick(pickedImageThumbnail);
@@ -690,8 +705,11 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath()); thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
defaultWallpaperExists = true; defaultWallpaperExists = true;
} else { } else {
Point defaultThumbSize = getDefaultThumbnailSize(getResources()); Resources res = getResources();
thumb = createThumbnail(defaultThumbSize, this, null, null, sysRes, resId, false); Point defaultThumbSize = getDefaultThumbnailSize(res);
int rotation = WallpaperCropActivity.getRotationFromExif(res, resId);
thumb = createThumbnail(
defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
if (thumb != null) { if (thumb != null) {
try { try {
defaultThumbFile.createNewFile(); defaultThumbFile.createNewFile();
@@ -63,7 +63,7 @@ public class TiledImageView extends FrameLayout {
// Guarded by locks // Guarded by locks
public float scale; public float scale;
public int centerX, centerY; public int centerX, centerY;
int rotation; public int rotation;
public TileSource source; public TileSource source;
Runnable isReadyCallback; Runnable isReadyCallback;