Loading packages/WallpaperCropper/src/com/android/photos/views/TiledImageView.java +1 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ public class TiledImageView extends FrameLayout { // Guarded by locks public float scale; public int centerX, centerY; int rotation; public int rotation; public TileSource source; Runnable isReadyCallback; Loading packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java +90 −17 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.wallpapercropper; import android.content.Context; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.util.AttributeSet; import android.util.FloatMath; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; Loading @@ -36,10 +38,18 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { private long mTouchDownTime; private float mFirstX, mFirstY; private float mLastX, mLastY; private float mCenterX, mCenterY; private float mMinScale; private boolean mTouchEnabled = true; 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; Matrix mRotateMatrix; Matrix mInverseRotateMatrix; public interface TouchCallback { void onTouchDown(); Loading @@ -54,17 +64,43 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { public CropView(Context context, AttributeSet attrs) { super(context, attrs); 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) { final float width = getWidth(); final float height = getHeight(); final float imageWidth = mRenderer.source.getImageWidth(); final float imageHeight = mRenderer.source.getImageHeight(); final float[] imageDims = getImageDims(); 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; float centerX = (width / 2f - mRenderer.centerX + (imageWidth - width) / 2f) float centerX = (width / 2f - rendererCenter[0] + (imageWidth - 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; float leftEdge = centerX - imageWidth / 2f * scale; float rightEdge = centerX + imageWidth / 2f * scale; Loading @@ -77,6 +113,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { edgesOut.bottom = bottomEdge; } public int getImageRotation() { return mRenderer.rotation; } public RectF getCrop() { final RectF edges = mTempEdges; getEdgesHelper(edges); Loading @@ -96,6 +136,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { public void setTileSource(TileSource source, Runnable 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); } Loading @@ -115,8 +161,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { mRenderer.scale = 1; } if (source != null) { mMinScale = Math.max(w / (float) source.getImageWidth(), h / (float) source.getImageHeight()); final float[] imageDims = getImageDims(); final float imageWidth = imageDims[0]; final float imageHeight = imageDims[1]; mMinScale = Math.max(w / imageWidth, h / imageHeight); mRenderer.scale = Math.max(mMinScale, mRenderer.scale); } } Loading Loading @@ -154,7 +202,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { final RectF edges = mTempEdges; getEdgesHelper(edges); 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) { Loading Loading @@ -215,8 +269,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { mScaleGestureDetector.onTouchEvent(event); switch (action) { case MotionEvent.ACTION_MOVE: mRenderer.centerX += (mLastX - x) / mRenderer.scale; mRenderer.centerY += (mLastY - y) / mRenderer.scale; float[] point = mTempPoint; point[0] = (mLastX - x) / mRenderer.scale; point[1] = (mLastY - y) / mRenderer.scale; mInverseRotateMatrix.mapPoints(point); mCenterX += point[0]; mCenterY += point[1]; updateCenter(); invalidate(); break; } Loading @@ -226,18 +285,32 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { final RectF edges = mTempEdges; getEdgesHelper(edges); 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) { mRenderer.centerX += Math.ceil(edges.left / scale); } if (edges.right < getWidth()) { mRenderer.centerX += (edges.right - getWidth()) / scale; adjustment[0] = edges.left / scale; } else if (edges.right < getWidth()) { adjustment[0] = (edges.right - getWidth()) / scale; } 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()) { mRenderer.centerY += (edges.bottom - getHeight()) / scale; for (int dim = 0; dim <= 1; dim++) { if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]); } mInverseRotateMatrix.mapPoints(adjustment); mCenterX += adjustment[0]; mCenterY += adjustment[1]; updateCenter(); } } Loading packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java +142 −42 Original line number Diff line number Diff line Loading @@ -37,12 +37,14 @@ import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.FloatMath; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import com.android.gallery3d.common.Utils; import com.android.gallery3d.exif.ExifInterface; import com.android.photos.BitmapRegionTileSource; import java.io.BufferedInputStream; Loading Loading @@ -85,10 +87,17 @@ public class WallpaperCropActivity extends Activity { mCropView = (CropView) findViewById(R.id.cropView); Intent cropIntent = this.getIntent(); Intent cropIntent = getIntent(); final Uri imageUri = cropIntent.getData(); mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, 0), null); if (imageUri == null) { Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); finish(); return; } int rotation = getRotationFromExif(this, imageUri); mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null); mCropView.setTouchEnabled(true); // Action bar // Show the custom action bar view Loading @@ -102,8 +111,6 @@ public class WallpaperCropActivity extends Activity { cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); } }); getWindow().addPrivateFlags( WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR); } public boolean enableRotation() { Loading Loading @@ -170,9 +177,47 @@ public class WallpaperCropActivity extends Activity { 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) { BitmapCropTask cropTask = new BitmapCropTask(this, filePath, null, 0, 0, true, false, null); int rotation = getRotationFromExif(filePath); BitmapCropTask cropTask = new BitmapCropTask( this, filePath, null, rotation, 0, 0, true, false, null); final Point bounds = cropTask.getImageBounds(); Runnable onEndCrop = new Runnable() { public void run() { Loading @@ -192,6 +237,7 @@ public class WallpaperCropActivity extends Activity { Resources res, int resId, final boolean finishActivityWhenDone) { // crop this image and scale it down to the default wallpaper size for // this device int rotation = getRotationFromExif(res, resId); Point inSize = mCropView.getSourceDimensions(); Point outSize = getDefaultWallpaperSize(getResources(), getWindowManager()); Loading @@ -209,8 +255,7 @@ public class WallpaperCropActivity extends Activity { } }; BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, crop, outSize.x, outSize.y, true, false, onEndCrop); crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); cropTask.execute(); } Loading @@ -222,8 +267,6 @@ public class WallpaperCropActivity extends Activity { protected void cropImageAndSetWallpaper(Uri uri, OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { // Get the crop Point inSize = mCropView.getSourceDimensions(); boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; Point minDims = new Point(); Loading Loading @@ -262,12 +305,21 @@ public class WallpaperCropActivity extends Activity { } // Get the crop RectF cropRect = mCropView.getCrop(); int cropRotation = mCropView.getImageRotation(); 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 // Extend the crop all the way to the right, for parallax // (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 float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width(); extraSpace = Math.min(extraSpace, maxExtraSpace); Loading @@ -285,7 +337,7 @@ public class WallpaperCropActivity extends Activity { float extraPortraitHeight = portraitHeight / cropScale - cropRect.height(); float expandHeight = Math.min(Math.min(inSize.y - cropRect.bottom, cropRect.top), Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), extraPortraitHeight / 2); cropRect.top -= expandHeight; cropRect.bottom += expandHeight; Loading @@ -303,7 +355,7 @@ public class WallpaperCropActivity extends Activity { } }; BitmapCropTask cropTask = new BitmapCropTask(this, uri, cropRect, outWidth, outHeight, true, false, onEndCrop); cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); if (onBitmapCroppedHandler != null) { cropTask.setOnBitmapCropped(onBitmapCroppedHandler); } Loading @@ -323,7 +375,7 @@ public class WallpaperCropActivity extends Activity { InputStream mInStream; RectF mCropBounds = null; int mOutWidth, mOutHeight; int mRotation = 0; // for now int mRotation; String mOutputFormat = "jpg"; // for now boolean mSetWallpaper; boolean mSaveCroppedBitmap; Loading @@ -334,40 +386,45 @@ public class WallpaperCropActivity extends Activity { boolean mNoCrop; 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) { mContext = c; mInFilePath = filePath; init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); } public BitmapCropTask(byte[] imageBytes, RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight, boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { mInImageBytes = imageBytes; init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); } 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) { mContext = c; 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, RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight, boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { mContext = c; mInResId = inResId; 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) { mCropBounds = cropBounds; mRotation = rotation; mOutWidth = outWidth; mOutHeight = outHeight; mSetWallpaper = setWallpaper; Loading Loading @@ -454,6 +511,29 @@ public class WallpaperCropActivity extends Activity { if (mInStream != null) { // Find crop bounds (scaled to original image size) 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); if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { Loading Loading @@ -497,6 +577,12 @@ public class WallpaperCropActivity extends Activity { fullSize = BitmapFactory.decodeStream(mInStream, null, options); } if (fullSize != null) { mCropBounds.left /= scaleDownSampleSize; mCropBounds.top /= scaleDownSampleSize; mCropBounds.bottom /= scaleDownSampleSize; mCropBounds.right /= scaleDownSampleSize; mCropBounds.roundOut(roundedTrueCrop); crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.height()); Loading @@ -508,16 +594,40 @@ public class WallpaperCropActivity extends Activity { failure = true; return false; } if (mOutWidth > 0 && mOutHeight > 0) { Matrix m = new Matrix(); RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); if (mRotation > 0) { m.setRotate(mRotation); m.mapRect(cropRect); if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; rotateMatrix.mapPoints(dimsAfter); dimsAfter[0] = Math.abs(dimsAfter[0]); dimsAfter[1] = Math.abs(dimsAfter[1]); 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); Matrix m = new Matrix(); if (mRotation == 0) { m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); m.preRotate(mRotation); } 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(), (int) returnRect.height(), Bitmap.Config.ARGB_8888); if (tmp != null) { Loading @@ -527,14 +637,6 @@ public class WallpaperCropActivity extends Activity { c.drawBitmap(crop, m, p); 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) { Loading Loading @@ -603,8 +705,7 @@ public class WallpaperCropActivity extends Activity { final SharedPreferences sharedPrefs, WindowManager windowManager, final WallpaperManager wallpaperManager) { final Point defaultWallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(res, windowManager); final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); new Thread("suggestWallpaperDimension") { public void run() { Loading @@ -616,7 +717,6 @@ public class WallpaperCropActivity extends Activity { }.start(); } protected static RectF getMaxCropRect( int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { RectF cropRect = new RectF(); Loading Loading
packages/WallpaperCropper/src/com/android/photos/views/TiledImageView.java +1 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ public class TiledImageView extends FrameLayout { // Guarded by locks public float scale; public int centerX, centerY; int rotation; public int rotation; public TileSource source; Runnable isReadyCallback; Loading
packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java +90 −17 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.wallpapercropper; import android.content.Context; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.util.AttributeSet; import android.util.FloatMath; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; Loading @@ -36,10 +38,18 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { private long mTouchDownTime; private float mFirstX, mFirstY; private float mLastX, mLastY; private float mCenterX, mCenterY; private float mMinScale; private boolean mTouchEnabled = true; 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; Matrix mRotateMatrix; Matrix mInverseRotateMatrix; public interface TouchCallback { void onTouchDown(); Loading @@ -54,17 +64,43 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { public CropView(Context context, AttributeSet attrs) { super(context, attrs); 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) { final float width = getWidth(); final float height = getHeight(); final float imageWidth = mRenderer.source.getImageWidth(); final float imageHeight = mRenderer.source.getImageHeight(); final float[] imageDims = getImageDims(); 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; float centerX = (width / 2f - mRenderer.centerX + (imageWidth - width) / 2f) float centerX = (width / 2f - rendererCenter[0] + (imageWidth - 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; float leftEdge = centerX - imageWidth / 2f * scale; float rightEdge = centerX + imageWidth / 2f * scale; Loading @@ -77,6 +113,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { edgesOut.bottom = bottomEdge; } public int getImageRotation() { return mRenderer.rotation; } public RectF getCrop() { final RectF edges = mTempEdges; getEdgesHelper(edges); Loading @@ -96,6 +136,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { public void setTileSource(TileSource source, Runnable 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); } Loading @@ -115,8 +161,10 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { mRenderer.scale = 1; } if (source != null) { mMinScale = Math.max(w / (float) source.getImageWidth(), h / (float) source.getImageHeight()); final float[] imageDims = getImageDims(); final float imageWidth = imageDims[0]; final float imageHeight = imageDims[1]; mMinScale = Math.max(w / imageWidth, h / imageHeight); mRenderer.scale = Math.max(mMinScale, mRenderer.scale); } } Loading Loading @@ -154,7 +202,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { final RectF edges = mTempEdges; getEdgesHelper(edges); 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) { Loading Loading @@ -215,8 +269,13 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { mScaleGestureDetector.onTouchEvent(event); switch (action) { case MotionEvent.ACTION_MOVE: mRenderer.centerX += (mLastX - x) / mRenderer.scale; mRenderer.centerY += (mLastY - y) / mRenderer.scale; float[] point = mTempPoint; point[0] = (mLastX - x) / mRenderer.scale; point[1] = (mLastY - y) / mRenderer.scale; mInverseRotateMatrix.mapPoints(point); mCenterX += point[0]; mCenterY += point[1]; updateCenter(); invalidate(); break; } Loading @@ -226,18 +285,32 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { final RectF edges = mTempEdges; getEdgesHelper(edges); 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) { mRenderer.centerX += Math.ceil(edges.left / scale); } if (edges.right < getWidth()) { mRenderer.centerX += (edges.right - getWidth()) / scale; adjustment[0] = edges.left / scale; } else if (edges.right < getWidth()) { adjustment[0] = (edges.right - getWidth()) / scale; } 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()) { mRenderer.centerY += (edges.bottom - getHeight()) / scale; for (int dim = 0; dim <= 1; dim++) { if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]); } mInverseRotateMatrix.mapPoints(adjustment); mCenterX += adjustment[0]; mCenterY += adjustment[1]; updateCenter(); } } Loading
packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java +142 −42 Original line number Diff line number Diff line Loading @@ -37,12 +37,14 @@ import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.FloatMath; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import com.android.gallery3d.common.Utils; import com.android.gallery3d.exif.ExifInterface; import com.android.photos.BitmapRegionTileSource; import java.io.BufferedInputStream; Loading Loading @@ -85,10 +87,17 @@ public class WallpaperCropActivity extends Activity { mCropView = (CropView) findViewById(R.id.cropView); Intent cropIntent = this.getIntent(); Intent cropIntent = getIntent(); final Uri imageUri = cropIntent.getData(); mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, 0), null); if (imageUri == null) { Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); finish(); return; } int rotation = getRotationFromExif(this, imageUri); mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null); mCropView.setTouchEnabled(true); // Action bar // Show the custom action bar view Loading @@ -102,8 +111,6 @@ public class WallpaperCropActivity extends Activity { cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); } }); getWindow().addPrivateFlags( WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR); } public boolean enableRotation() { Loading Loading @@ -170,9 +177,47 @@ public class WallpaperCropActivity extends Activity { 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) { BitmapCropTask cropTask = new BitmapCropTask(this, filePath, null, 0, 0, true, false, null); int rotation = getRotationFromExif(filePath); BitmapCropTask cropTask = new BitmapCropTask( this, filePath, null, rotation, 0, 0, true, false, null); final Point bounds = cropTask.getImageBounds(); Runnable onEndCrop = new Runnable() { public void run() { Loading @@ -192,6 +237,7 @@ public class WallpaperCropActivity extends Activity { Resources res, int resId, final boolean finishActivityWhenDone) { // crop this image and scale it down to the default wallpaper size for // this device int rotation = getRotationFromExif(res, resId); Point inSize = mCropView.getSourceDimensions(); Point outSize = getDefaultWallpaperSize(getResources(), getWindowManager()); Loading @@ -209,8 +255,7 @@ public class WallpaperCropActivity extends Activity { } }; BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, crop, outSize.x, outSize.y, true, false, onEndCrop); crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); cropTask.execute(); } Loading @@ -222,8 +267,6 @@ public class WallpaperCropActivity extends Activity { protected void cropImageAndSetWallpaper(Uri uri, OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { // Get the crop Point inSize = mCropView.getSourceDimensions(); boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; Point minDims = new Point(); Loading Loading @@ -262,12 +305,21 @@ public class WallpaperCropActivity extends Activity { } // Get the crop RectF cropRect = mCropView.getCrop(); int cropRotation = mCropView.getImageRotation(); 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 // Extend the crop all the way to the right, for parallax // (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 float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width(); extraSpace = Math.min(extraSpace, maxExtraSpace); Loading @@ -285,7 +337,7 @@ public class WallpaperCropActivity extends Activity { float extraPortraitHeight = portraitHeight / cropScale - cropRect.height(); float expandHeight = Math.min(Math.min(inSize.y - cropRect.bottom, cropRect.top), Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), extraPortraitHeight / 2); cropRect.top -= expandHeight; cropRect.bottom += expandHeight; Loading @@ -303,7 +355,7 @@ public class WallpaperCropActivity extends Activity { } }; BitmapCropTask cropTask = new BitmapCropTask(this, uri, cropRect, outWidth, outHeight, true, false, onEndCrop); cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); if (onBitmapCroppedHandler != null) { cropTask.setOnBitmapCropped(onBitmapCroppedHandler); } Loading @@ -323,7 +375,7 @@ public class WallpaperCropActivity extends Activity { InputStream mInStream; RectF mCropBounds = null; int mOutWidth, mOutHeight; int mRotation = 0; // for now int mRotation; String mOutputFormat = "jpg"; // for now boolean mSetWallpaper; boolean mSaveCroppedBitmap; Loading @@ -334,40 +386,45 @@ public class WallpaperCropActivity extends Activity { boolean mNoCrop; 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) { mContext = c; mInFilePath = filePath; init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); } public BitmapCropTask(byte[] imageBytes, RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight, boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { mInImageBytes = imageBytes; init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); init(cropBounds, rotation, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); } 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) { mContext = c; 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, RectF cropBounds, int outWidth, int outHeight, RectF cropBounds, int rotation, int outWidth, int outHeight, boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { mContext = c; mInResId = inResId; 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) { mCropBounds = cropBounds; mRotation = rotation; mOutWidth = outWidth; mOutHeight = outHeight; mSetWallpaper = setWallpaper; Loading Loading @@ -454,6 +511,29 @@ public class WallpaperCropActivity extends Activity { if (mInStream != null) { // Find crop bounds (scaled to original image size) 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); if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { Loading Loading @@ -497,6 +577,12 @@ public class WallpaperCropActivity extends Activity { fullSize = BitmapFactory.decodeStream(mInStream, null, options); } if (fullSize != null) { mCropBounds.left /= scaleDownSampleSize; mCropBounds.top /= scaleDownSampleSize; mCropBounds.bottom /= scaleDownSampleSize; mCropBounds.right /= scaleDownSampleSize; mCropBounds.roundOut(roundedTrueCrop); crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.height()); Loading @@ -508,16 +594,40 @@ public class WallpaperCropActivity extends Activity { failure = true; return false; } if (mOutWidth > 0 && mOutHeight > 0) { Matrix m = new Matrix(); RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); if (mRotation > 0) { m.setRotate(mRotation); m.mapRect(cropRect); if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; rotateMatrix.mapPoints(dimsAfter); dimsAfter[0] = Math.abs(dimsAfter[0]); dimsAfter[1] = Math.abs(dimsAfter[1]); 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); Matrix m = new Matrix(); if (mRotation == 0) { m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); m.preRotate(mRotation); } 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(), (int) returnRect.height(), Bitmap.Config.ARGB_8888); if (tmp != null) { Loading @@ -527,14 +637,6 @@ public class WallpaperCropActivity extends Activity { c.drawBitmap(crop, m, p); 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) { Loading Loading @@ -603,8 +705,7 @@ public class WallpaperCropActivity extends Activity { final SharedPreferences sharedPrefs, WindowManager windowManager, final WallpaperManager wallpaperManager) { final Point defaultWallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(res, windowManager); final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); new Thread("suggestWallpaperDimension") { public void run() { Loading @@ -616,7 +717,6 @@ public class WallpaperCropActivity extends Activity { }.start(); } protected static RectF getMaxCropRect( int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { RectF cropRect = new RectF(); Loading