summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow
diff options
context:
space:
mode:
authorRuben Brunk <rubenbrunk@google.com>2012-09-27 12:38:08 -0700
committerRuben Brunk <rubenbrunk@google.com>2012-10-08 12:50:43 -0700
commit98c69b734e8c1e2a3c3d8c180cfcfdc4bbac0182 (patch)
tree7abf0c2e4c2e68de88061ca497c180e34eee76cb /src/com/android/gallery3d/filtershow
parent96e48d0946d3b3178a784f92fb40f21307cdd518 (diff)
downloadandroid_packages_apps_Snap-98c69b734e8c1e2a3c3d8c180cfcfdc4bbac0182.tar.gz
android_packages_apps_Snap-98c69b734e8c1e2a3c3d8c180cfcfdc4bbac0182.tar.bz2
android_packages_apps_Snap-98c69b734e8c1e2a3c3d8c180cfcfdc4bbac0182.zip
Adding Crop, Rotate, Flip.
Bug: 7224232 Bug: 7218935 Adding geometry manipulation UI features. Change-Id: If924313c18121e6d192a1934e76691bd578d8eb0
Diffstat (limited to 'src/com/android/gallery3d/filtershow')
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java25
-rw-r--r--src/com/android/gallery3d/filtershow/PanelController.java36
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java222
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java354
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java179
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java559
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java161
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java88
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSlave.java21
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java217
-rw-r--r--src/com/android/gallery3d/filtershow/presets/ImagePreset.java41
11 files changed, 1686 insertions, 217 deletions
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index 89ef41744..664b67f5c 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -38,6 +38,9 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
import com.android.gallery3d.filtershow.imageshow.ImageStraighten;
import com.android.gallery3d.filtershow.imageshow.ImageZoom;
+import com.android.gallery3d.filtershow.imageshow.ImageFlip;
+import com.android.gallery3d.filtershow.imageshow.ImageCrop;
+import com.android.gallery3d.filtershow.imageshow.ImageRotate;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.presets.ImagePresetBW;
import com.android.gallery3d.filtershow.presets.ImagePresetBWBlue;
@@ -65,6 +68,9 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
private ImageBorder mImageBorders = null;
private ImageStraighten mImageStraighten = null;
private ImageZoom mImageZoom = null;
+ private ImageCrop mImageCrop = null;
+ private ImageRotate mImageRotate = null;
+ private ImageFlip mImageFlip = null;
private View mListFx = null;
private View mListBorders = null;
@@ -123,12 +129,18 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageBorders = (ImageBorder) findViewById(R.id.imageBorder);
mImageStraighten = (ImageStraighten) findViewById(R.id.imageStraighten);
mImageZoom = (ImageZoom) findViewById(R.id.imageZoom);
+ mImageCrop = (ImageCrop) findViewById(R.id.imageCrop);
+ mImageRotate = (ImageRotate) findViewById(R.id.imageRotate);
+ mImageFlip = (ImageFlip) findViewById(R.id.imageFlip);
mImageViews.add(mImageShow);
mImageViews.add(mImageCurves);
mImageViews.add(mImageBorders);
mImageViews.add(mImageStraighten);
mImageViews.add(mImageZoom);
+ mImageViews.add(mImageCrop);
+ mImageViews.add(mImageRotate);
+ mImageViews.add(mImageFlip);
mListFx = findViewById(R.id.fxList);
mListBorders = findViewById(R.id.bordersList);
@@ -155,11 +167,20 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageStraighten.setMaster(mImageShow);
mImageZoom.setImageLoader(mImageLoader);
mImageZoom.setMaster(mImageShow);
+ mImageCrop.setImageLoader(mImageLoader);
+ mImageCrop.setMaster(mImageShow);
+ mImageRotate.setImageLoader(mImageLoader);
+ mImageRotate.setMaster(mImageShow);
+ mImageFlip.setImageLoader(mImageLoader);
+ mImageFlip.setMaster(mImageShow);
mPanelController.addImageView(findViewById(R.id.imageShow));
mPanelController.addImageView(findViewById(R.id.imageCurves));
mPanelController.addImageView(findViewById(R.id.imageBorder));
mPanelController.addImageView(findViewById(R.id.imageStraighten));
+ mPanelController.addImageView(findViewById(R.id.imageCrop));
+ mPanelController.addImageView(findViewById(R.id.imageRotate));
+ mPanelController.addImageView(findViewById(R.id.imageFlip));
mPanelController.addImageView(findViewById(R.id.imageZoom));
mPanelController.addPanel(mFxButton, mListFx, 0);
@@ -204,12 +225,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
seekBar.setMax(200);
mImageShow.setSeekBar(seekBar);
mPanelController.setRowPanel(findViewById(R.id.secondRowPanel));
- mPanelController.setUtilityPanel(findViewById(R.id.filterButtonsList),
+ mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList),
findViewById(R.id.compareWithOriginalImage),
findViewById(R.id.applyEffect));
mPanelController.setMasterImage(mImageShow);
mPanelController.setCurrentPanel(mFxButton);
-
Intent intent = getIntent();
String data = intent.getDataString();
if (data != null) {
@@ -420,6 +440,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
public void invalidateViews() {
for (ImageShow views : mImageViews) {
views.invalidate();
+ views.updateImage();
}
}
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index f9a1f1d16..dbb6f27b3 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -1,6 +1,7 @@
package com.android.gallery3d.filtershow;
+import android.content.Context;
import android.text.Html;
import android.view.View;
import android.view.View.OnClickListener;
@@ -103,14 +104,17 @@ public class PanelController implements OnClickListener {
}
class UtilityPanel {
+ private final Context mContext;
private final View mView;
private final View mCompareView;
private final TextView mTextView;
private boolean mSelected = false;
private String mEffectName = null;
private int mParameterValue = 0;
+ private boolean mShowParameterValue = false;
- public UtilityPanel(View view, View compareView, View textView) {
+ public UtilityPanel(Context context, View view, View compareView, View textView) {
+ mContext = context;
mView = view;
mCompareView = compareView;
mTextView = (TextView) textView;
@@ -135,12 +139,22 @@ public class PanelController implements OnClickListener {
public void setEffectName(String effectName) {
mEffectName = effectName;
+ showParameter(true);
updateText();
}
+ public void showParameter(boolean s) {
+ mShowParameterValue = s;
+ }
+
public void updateText() {
- mTextView.setText(Html.fromHtml("Apply" + "<br/><small>" + mEffectName + "<br/>"
- + mParameterValue + "</small>"));
+ String apply = mContext.getString(R.string.apply_effect);
+ if (mShowParameterValue) {
+ mTextView.setText(Html.fromHtml(apply + "<br/><small>" + mEffectName + "<br/>"
+ + mParameterValue + "</small>"));
+ } else {
+ mTextView.setText(Html.fromHtml(apply + "<br/><small>" + mEffectName + "</small>"));
+ }
}
public ViewPropertyAnimator unselect() {
@@ -227,6 +241,10 @@ public class PanelController implements OnClickListener {
mUtilityPanel.onNewValue(value);
}
+ public void showParameter(boolean s) {
+ mUtilityPanel.showParameter(s);
+ }
+
public void setCurrentPanel(View panel) {
showPanel(panel);
}
@@ -235,8 +253,8 @@ public class PanelController implements OnClickListener {
mRowPanel = rowPanel;
}
- public void setUtilityPanel(View utilityPanel, View compareView, View textView) {
- mUtilityPanel = new UtilityPanel(utilityPanel, compareView, textView);
+ public void setUtilityPanel(Context context, View utilityPanel, View compareView, View textView) {
+ mUtilityPanel = new UtilityPanel(context, utilityPanel, compareView, textView);
}
public void setMasterImage(ImageShow imageShow) {
@@ -376,20 +394,22 @@ public class PanelController implements OnClickListener {
break;
}
case R.id.cropButton: {
- mCurrentImage = showImageView(R.id.imageShow);
+ mCurrentImage = showImageView(R.id.imageCrop);
mUtilityPanel.setEffectName("Crop");
+ mUtilityPanel.showParameter(false);
mUtilityPanel.setGeometryEffect(true);
break;
}
case R.id.rotateButton: {
- mCurrentImage = showImageView(R.id.imageShow);
+ mCurrentImage = showImageView(R.id.imageRotate);
mUtilityPanel.setEffectName("Rotate");
mUtilityPanel.setGeometryEffect(true);
break;
}
case R.id.flipButton: {
- mCurrentImage = showImageView(R.id.imageShow);
+ mCurrentImage = showImageView(R.id.imageFlip);
mUtilityPanel.setEffectName("Flip");
+ mUtilityPanel.showParameter(false);
mUtilityPanel.setGeometryEffect(true);
break;
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
new file mode 100644
index 000000000..1f166255a
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -0,0 +1,222 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
+
+import android.graphics.RectF;
+
+/**
+ * This class holds metadata about an image's geometry. Specifically: rotation,
+ * scaling, cropping, and image boundaries. It maintains the invariant that the
+ * cropping boundaries are within or equal to the image boundaries (before
+ * rotation) WHEN mSafe is true.
+ */
+
+public class GeometryMetadata {
+ // Applied in order: rotate, crop, scale.
+ // Do not scale saved image (presumably?).
+ private float mScaleFactor = 0;
+ private float mRotation = 0;
+ private float mStraightenRotation = 0;
+ private final RectF mCropBounds = new RectF();
+ private final RectF mPhotoBounds = new RectF();
+ private FLIP mFlip = FLIP.NONE;
+ private boolean mSafe = false;
+
+ public enum FLIP {
+ NONE, VERTICAL, HORIZONTAL, BOTH
+ }
+
+ public GeometryMetadata() {
+ }
+
+ public GeometryMetadata(GeometryMetadata g) {
+ set(g);
+ }
+
+ public GeometryMetadata(float scale, float rotation, float straighten, RectF cropBounds,
+ RectF photoBounds, FLIP flipType) {
+ mScaleFactor = scale;
+ mRotation = rotation;
+ mStraightenRotation = straighten;
+ mCropBounds.set(cropBounds);
+ mPhotoBounds.set(photoBounds);
+ mFlip = flipType;
+ mSafe = cropFitsInPhoto(mCropBounds);
+ }
+
+ // Safe as long as invariant holds.
+ public void set(GeometryMetadata g) {
+ mScaleFactor = g.mScaleFactor;
+ mRotation = g.mRotation;
+ mStraightenRotation = g.mStraightenRotation;
+ mCropBounds.set(g.mCropBounds);
+ mPhotoBounds.set(g.mPhotoBounds);
+ mFlip = g.mFlip;
+ mSafe = g.mSafe;
+ }
+
+ public void safeSet(GeometryMetadata g) {
+ if (g.safe()) {
+ set(g);
+ return;
+ }
+
+ mScaleFactor = g.mScaleFactor;
+ mRotation = g.mRotation;
+ mStraightenRotation = g.mStraightenRotation;
+ mCropBounds.set(g.mCropBounds);
+ safeSetPhotoBounds(g.mPhotoBounds);
+ mFlip = g.mFlip;
+ }
+
+ public void safeSet(float scale,
+ float rotation,
+ float straighten,
+ RectF cropBounds,
+ RectF photoBounds,
+ FLIP flipType) {
+ mScaleFactor = scale;
+ mStraightenRotation = straighten;
+ mRotation = rotation;
+ mCropBounds.set(cropBounds);
+ safeSetPhotoBounds(photoBounds);
+ mFlip = flipType;
+ }
+
+ public float getScaleFactor() {
+ return mScaleFactor;
+ }
+
+ public float getRotation() {
+ return mRotation;
+ }
+
+ public float getStraightenRotation() {
+ return mStraightenRotation;
+ }
+
+ public RectF getCropBounds() {
+ return new RectF(mCropBounds);
+ }
+
+ public FLIP getFlipType() {
+ return mFlip;
+ }
+
+ public RectF getPhotoBounds() {
+ return new RectF(mPhotoBounds);
+ }
+
+ public boolean safe() {
+ return mSafe;
+ }
+
+ public void setScaleFactor(float scale) {
+ mScaleFactor = scale;
+ }
+
+ public void setFlipType(FLIP flip) {
+ mFlip = flip;
+ }
+
+ public void setRotation(float rotation) {
+ mRotation = rotation;
+ }
+
+ public void setStraightenRotation(float straighten) {
+ mStraightenRotation = straighten;
+ }
+
+ /**
+ * Sets crop bounds to be the intersection of mPhotoBounds and the new crop
+ * bounds. If there was no intersection, returns false and does not set crop
+ * bounds
+ */
+ public boolean safeSetCropBounds(RectF newCropBounds) {
+ if (mCropBounds.setIntersect(newCropBounds, mPhotoBounds)) {
+ mSafe = true;
+ return true;
+ }
+ return false;
+ }
+
+ public void setCropBounds(RectF newCropBounds) {
+ mCropBounds.set(newCropBounds);
+ mSafe = false;
+ }
+
+ /**
+ * Sets mPhotoBounds to be the new photo bounds and sets mCropBounds to be
+ * the intersection of the new photo bounds and the old crop bounds. Sets
+ * the crop bounds to mPhotoBounds if there is no intersection.
+ */
+
+ public void safeSetPhotoBounds(RectF newPhotoBounds) {
+ mPhotoBounds.set(newPhotoBounds);
+ if (!mCropBounds.intersect(mPhotoBounds)) {
+ mCropBounds.set(mPhotoBounds);
+ }
+ mSafe = true;
+ }
+
+ public void setPhotoBounds(RectF newPhotoBounds) {
+ mPhotoBounds.set(newPhotoBounds);
+ mSafe = false;
+ }
+
+ public boolean cropFitsInPhoto(RectF cropBounds) {
+ return mPhotoBounds.contains(cropBounds);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ GeometryMetadata d = (GeometryMetadata) o;
+ return (mScaleFactor == d.mScaleFactor &&
+ mRotation == d.mRotation &&
+ mStraightenRotation == d.mStraightenRotation &&
+ mFlip == d.mFlip && mSafe == d.mSafe &&
+ mCropBounds.equals(d.mCropBounds) && mPhotoBounds.equals(d.mPhotoBounds));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 23;
+ result = 31 * result + Float.floatToIntBits(mRotation);
+ result = 31 * result + Float.floatToIntBits(mStraightenRotation);
+ result = 31 * result + Float.floatToIntBits(mScaleFactor);
+ result = 31 * result + mFlip.hashCode();
+ result = 31 * result + mCropBounds.hashCode();
+ result = 31 * result + mPhotoBounds.hashCode();
+ result = 31 * result + (mSafe ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[" + "scale=" + mScaleFactor
+ + ",rotation=" + mRotation + ",flip=" + mFlip + ",safe="
+ + (mSafe ? "true" : "false") + ",straighten="
+ + mStraightenRotation + ",cropRect=" + mCropBounds.toShortString()
+ + ",photoRect=" + mPhotoBounds.toShortString() + "]";
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
new file mode 100644
index 000000000..836ec82f8
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -0,0 +1,354 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import com.android.gallery3d.R;
+
+import com.android.gallery3d.filtershow.presets.ImagePreset;
+
+public class ImageCrop extends ImageGeometry {
+ private static final boolean LOGV = false;
+ private static final int MOVE_LEFT = 1;
+ private static final int MOVE_TOP = 2;
+ private static final int MOVE_RIGHT = 4;
+ private static final int MOVE_BOTTOM = 8;
+ private static final int MOVE_BLOCK = 16;
+
+ private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
+ private static final int TOUCH_TOLERANCE = 30;
+ private static final int SHADOW_ALPHA = 160;
+
+ private float mAspectWidth = 4;
+ private float mAspectHeight = 3;
+ private boolean mFixAspectRatio = false; // not working yet
+
+ private final Paint borderPaint;
+
+ private float mCropOffsetX = 0;
+ private float mCropOffsetY = 0;
+ private float mPrevOffsetX = 0;
+ private float mPrevOffsetY = 0;
+
+ private int movingEdges;
+ private final Drawable cropIndicator;
+ private final int indicatorSize;
+
+ private static final String LOGTAG = "ImageCrop";
+
+ private static final Paint gPaint = new Paint();
+
+ public ImageCrop(Context context) {
+ super(context);
+ Resources resources = context.getResources();
+ cropIndicator = resources.getDrawable(R.drawable.camera_crop_holo);
+ indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
+ int borderColor = resources.getColor(R.color.opaque_cyan);
+ borderPaint = new Paint();
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setColor(borderColor);
+ borderPaint.setStrokeWidth(2f);
+ }
+
+ public ImageCrop(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Resources resources = context.getResources();
+ cropIndicator = resources.getDrawable(R.drawable.camera_crop_holo);
+ indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
+ int borderColor = resources.getColor(R.color.opaque_cyan);
+ borderPaint = new Paint();
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setColor(borderColor);
+ borderPaint.setStrokeWidth(2f);
+ }
+
+ private float getScaledMinWidthHeight() {
+ RectF disp = getLocalDisplayBounds();
+ float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
+ / getLocalScale();
+ return scaled;
+ }
+
+ protected static Matrix getCropRotationMatrix(float rotation, RectF localImage) {
+ Matrix m = new Matrix();
+ m.setRotate(rotation, localImage.centerX(), localImage.centerY());
+ if (!m.rectStaysRect()) {
+ return null;
+ }
+ return m;
+ }
+
+ protected RectF getCropBoundsDisplayed() {
+ RectF bounds = getLocalCropBounds();
+ RectF crop = new RectF(bounds);
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+
+ if (m == null) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
+ m = new Matrix();
+ } else {
+ m.mapRect(crop);
+ }
+ m = new Matrix();
+ float zoom = getLocalScale();
+ m.setScale(zoom, zoom, mCenterX, mCenterY);
+ m.preTranslate(mXOffset, mYOffset);
+ m.mapRect(crop);
+ return crop;
+ }
+
+ private RectF getRotatedCropBounds() {
+ RectF bounds = getLocalCropBounds();
+ RectF crop = new RectF(bounds);
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+
+ if (m == null) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
+ return null;
+ } else {
+ m.mapRect(crop);
+ }
+ return crop;
+ }
+
+ /**
+ * Sets cropped bounds; modifies the bounds if it's smaller than the allowed
+ * dimensions.
+ */
+ public void setCropBounds(RectF bounds) {
+ // Avoid cropping smaller than minimum width or height.
+ RectF cbounds = new RectF(bounds);
+ float minWidthHeight = getScaledMinWidthHeight();
+
+ float newWidth = cbounds.width();
+ float newHeight = cbounds.height();
+ if (newWidth < minWidthHeight) {
+ newWidth = minWidthHeight;
+ }
+ if (newHeight < minWidthHeight) {
+ newHeight = minWidthHeight;
+ }
+
+ RectF pbounds = getLocalPhotoBounds();
+ if (pbounds.width() < minWidthHeight) {
+ newWidth = pbounds.width();
+ }
+ if (pbounds.height() < minWidthHeight) {
+ newHeight = pbounds.height();
+ }
+
+ cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
+ RectF snappedCrop = findCropBoundForRotatedImg(cbounds, pbounds, getLocalStraighten(),
+ mCenterX - mXOffset, mCenterY - mYOffset);
+ if (mFixAspectRatio) {
+ // TODO: add aspect ratio stuff
+ fixAspectRatio(snappedCrop, mAspectWidth, mAspectHeight);
+ }
+ setLocalCropBounds(snappedCrop);
+ invalidate();
+ }
+
+ private void detectMovingEdges(float x, float y) {
+ RectF cropped = getCropBoundsDisplayed();
+ movingEdges = 0;
+
+ // Check left or right.
+ float left = Math.abs(x - cropped.left);
+ float right = Math.abs(x - cropped.right);
+ if ((left <= TOUCH_TOLERANCE) && (left < right)) {
+ movingEdges |= MOVE_LEFT;
+ }
+ else if (right <= TOUCH_TOLERANCE) {
+ movingEdges |= MOVE_RIGHT;
+ }
+
+ // Check top or bottom.
+ float top = Math.abs(y - cropped.top);
+ float bottom = Math.abs(y - cropped.bottom);
+ if ((top <= TOUCH_TOLERANCE) & (top < bottom)) {
+ movingEdges |= MOVE_TOP;
+ }
+ else if (bottom <= TOUCH_TOLERANCE) {
+ movingEdges |= MOVE_BOTTOM;
+ }
+ invalidate();
+ }
+
+ private void moveEdges(float dX, float dY) {
+ RectF cropped = getRotatedCropBounds();
+ float minWidthHeight = getScaledMinWidthHeight();
+ float scale = getLocalScale();
+ float deltaX = dX / scale;
+ float deltaY = dY / scale;
+ if (movingEdges == MOVE_BLOCK) {
+ // TODO
+ } else {
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ cropped.left = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight);
+ fixRectAspectW(cropped);
+ }
+ if ((movingEdges & MOVE_TOP) != 0) {
+ cropped.top = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight);
+ fixRectAspectH(cropped);
+ }
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ cropped.right = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight);
+ fixRectAspectW(cropped);
+ }
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ cropped.bottom = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight);
+ fixRectAspectH(cropped);
+ }
+ }
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0)) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
+ }
+ if (!m0.mapRect(cropped)) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
+ }
+ setCropBounds(cropped);
+ }
+
+ private void fixRectAspectH(RectF cropped) {
+ if (mFixAspectRatio) {
+ float half = getNewWidthForHeightAspect(cropped.height(), mAspectWidth, mAspectHeight) / 2;
+ float mid = (cropped.right - cropped.left) / 2;
+ cropped.left = mid - half;
+ cropped.right = mid + half;
+ }
+ }
+
+ private void fixRectAspectW(RectF cropped) {
+ if (mFixAspectRatio) {
+ float half = getNewHeightForWidthAspect(cropped.width(), mAspectWidth, mAspectHeight) / 2;
+ float mid = (cropped.bottom - cropped.top) / 2;
+ cropped.top = mid - half;
+ cropped.bottom = mid + half;
+ }
+ }
+
+ private void drawShadow(Canvas canvas, float left, float top, float right, float bottom) {
+ canvas.save();
+ canvas.clipRect(left, top, right, bottom);
+ canvas.drawARGB(SHADOW_ALPHA, 0, 0, 0);
+ canvas.restore();
+ }
+
+ private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
+ int left = (int) centerX - indicatorSize / 2;
+ int top = (int) centerY - indicatorSize / 2;
+ indicator.setBounds(left, top, left + indicatorSize, top + indicatorSize);
+ indicator.draw(canvas);
+ }
+
+ @Override
+ protected void setActionDown(float x, float y) {
+ super.setActionDown(x, y);
+ detectMovingEdges(x, y);
+ if (movingEdges == 0) {
+ mPrevOffsetX = mCropOffsetX;
+ mPrevOffsetY = mCropOffsetY;
+ }
+ }
+
+ @Override
+ protected void setActionMove(float x, float y) {
+ if (movingEdges != 0) {
+ moveEdges(x - mCurrentX, y - mCurrentY);
+ } else {
+ float dx = x - mTouchCenterX;
+ float dy = y - mTouchCenterY;
+ mCropOffsetX = dx + mPrevOffsetX;
+ mCropOffsetY = dy + mPrevOffsetY;
+ }
+ super.setActionMove(x, y);
+ }
+
+ @Override
+ protected void gainedVisibility() {
+ setCropBounds(getLocalCropBounds());
+ }
+
+ @Override
+ protected void lostVisibility() {
+ }
+
+ @Override
+ protected void drawShape(Canvas canvas, Bitmap image) {
+ gPaint.setAntiAlias(true);
+ gPaint.setFilterBitmap(true);
+ gPaint.setDither(true);
+ gPaint.setARGB(255, 255, 255, 255);
+
+ drawRegularFlippedBitmap(canvas, image, gPaint);
+
+ RectF displayRect = getLocalDisplayBounds();
+ float dWidth = displayRect.width();
+ float dHeight = displayRect.height();
+ RectF boundsRect = getCropBoundsDisplayed();
+ gPaint.setARGB(128, 0, 0, 0);
+ gPaint.setStyle(Paint.Style.FILL);
+ // TODO: move this to style when refactoring
+ canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint);
+ canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint);
+ canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom,
+ gPaint);
+ canvas.drawRect(boundsRect.right, boundsRect.top, dWidth,
+ boundsRect.bottom, gPaint);
+
+ Path path = new Path();
+ path.addRect(boundsRect, Path.Direction.CCW);
+ gPaint.setARGB(255, 255, 255, 255);
+ gPaint.setStrokeWidth(3);
+ gPaint.setStyle(Paint.Style.STROKE);
+
+ canvas.drawPath(path, gPaint);
+
+ boolean notMoving = movingEdges == 0;
+ if (((movingEdges & MOVE_TOP) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, boundsRect.centerX(), boundsRect.top);
+ }
+ if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, boundsRect.centerX(), boundsRect.bottom);
+ }
+ if (((movingEdges & MOVE_LEFT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, boundsRect.left, boundsRect.centerY());
+ }
+ if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, boundsRect.right, boundsRect.centerY());
+ }
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java
new file mode 100644
index 000000000..ba01d888a
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java
@@ -0,0 +1,179 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
+
+public class ImageFlip extends ImageGeometry {
+
+ private static final Paint gPaint = new Paint();
+ private static final float MIN_FLICK_DIST_FOR_FLIP = 0.2f;
+ private static final String LOGTAG = "ImageFlip";
+ private FLIP mNextFlip = FLIP.NONE;
+
+ public ImageFlip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageFlip(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void setActionDown(float x, float y) {
+ super.setActionDown(x, y);
+ }
+
+ @Override
+ protected void setActionMove(float x, float y) {
+ super.setActionMove(x, y);
+
+ float diffx = mTouchCenterX - x;
+ float diffy = mTouchCenterY - y;
+ float flick = getScaledMinFlick();
+ if (Math.abs(diffx) >= flick) {
+ // flick moving left/right
+ FLIP flip = getLocalFlip();
+ switch (flip) {
+ case NONE:
+ flip = FLIP.HORIZONTAL;
+ break;
+ case HORIZONTAL:
+ flip = FLIP.NONE;
+ break;
+ case VERTICAL:
+ flip = FLIP.BOTH;
+ break;
+ case BOTH:
+ flip = FLIP.VERTICAL;
+ break;
+ default:
+ flip = FLIP.NONE;
+ break;
+ }
+ mNextFlip = flip;
+ }
+ if (Math.abs(diffy) >= flick) {
+ // flick moving up/down
+ FLIP flip = getLocalFlip();
+ switch (flip) {
+ case NONE:
+ flip = FLIP.VERTICAL;
+ break;
+ case VERTICAL:
+ flip = FLIP.NONE;
+ break;
+ case HORIZONTAL:
+ flip = FLIP.BOTH;
+ break;
+ case BOTH:
+ flip = FLIP.HORIZONTAL;
+ break;
+ default:
+ flip = FLIP.NONE;
+ break;
+ }
+ mNextFlip = flip;
+ }
+ }
+
+ @Override
+ protected void setActionUp() {
+ super.setActionUp();
+ setLocalFlip(mNextFlip);
+ }
+
+ @Override
+ public void resetParameter() {
+ super.resetParameter();
+ mNextFlip = FLIP.NONE;
+ }
+
+ private float getScaledMinFlick() {
+ RectF disp = getLocalDisplayBounds();
+ float scaled = Math.min(disp.width(), disp.height()) * MIN_FLICK_DIST_FOR_FLIP
+ / getLocalScale();
+ return scaled;
+ }
+
+ @Override
+ protected void drawShape(Canvas canvas, Bitmap image) {
+ gPaint.setAntiAlias(true);
+ gPaint.setFilterBitmap(true);
+ gPaint.setDither(true);
+ gPaint.setARGB(255, 255, 255, 255);
+
+ FLIP flip = mNextFlip;
+ canvas.save();
+ float zoom = getLocalScale();
+ canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY);
+ canvas.scale(zoom, zoom, mCenterX, mCenterY);
+ canvas.translate(mXOffset, mYOffset);
+ if (flip == FLIP.HORIZONTAL) {
+ Matrix flipper = getHorizontalMatrix(image.getWidth());
+ canvas.drawBitmap(image, flipper, gPaint);
+ } else if (flip == FLIP.VERTICAL) {
+ Matrix flipper = getVerticalMatrix(image.getHeight());
+ canvas.drawBitmap(image, flipper, gPaint);
+ } else if (flip == FLIP.BOTH) {
+ Matrix flipper = getVerticalMatrix(image.getHeight());
+ flipper.postConcat(getHorizontalMatrix(image.getWidth()));
+ canvas.drawBitmap(image, flipper, gPaint);
+ } else {
+ canvas.drawBitmap(image, 0, 0, gPaint);
+ }
+ canvas.restore();
+
+ RectF cropBounds = getCropBoundsDisplayed(getLocalCropBounds());
+
+ Matrix m0 = new Matrix();
+ m0.setRotate(getLocalRotation(), mCenterX, mCenterY);
+ float[] corners = getCornersFromRect(cropBounds);
+ m0.mapPoints(corners);
+ gPaint.setARGB(255, 255, 255, 255);
+ // TODO: pull out style to xml
+ gPaint.setStrokeWidth(3);
+ gPaint.setStyle(Paint.Style.STROKE);
+ drawClosedPath(canvas, gPaint, corners);
+
+ canvas.save();
+ canvas.rotate(getLocalRotation(), mCenterX, mCenterY);
+ RectF displayRect = getLocalDisplayBounds();
+ float dWidth = displayRect.width();
+ float dHeight = displayRect.height();
+ RectF boundsRect = cropBounds;
+ gPaint.setARGB(128, 0, 0, 0);
+ gPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint);
+ canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint);
+ canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom,
+ gPaint);
+ canvas.drawRect(boundsRect.right, boundsRect.top, dWidth,
+ boundsRect.bottom, gPaint);
+ canvas.rotate(-getLocalRotation(), mCenterX, mCenterY);
+ canvas.restore();
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
new file mode 100644
index 000000000..c6709e81f
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -0,0 +1,559 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
+import com.android.gallery3d.filtershow.presets.ImagePreset;
+
+public abstract class ImageGeometry extends ImageSlave {
+ private boolean mVisibilityGained = false;
+ private boolean mHasDrawn = false;
+
+ protected static final float MAX_STRAIGHTEN_ANGLE = 45;
+ protected static final float MIN_STRAIGHTEN_ANGLE = -45;
+
+ protected float mCenterX;
+ protected float mCenterY;
+
+ protected float mCurrentX;
+ protected float mCurrentY;
+ protected float mTouchCenterX;
+ protected float mTouchCenterY;
+
+ // Local geometry data
+ private GeometryMetadata mLocalGeoMetadata = null;
+ private RectF mLocalDisplayBounds = null;
+ protected float mXOffset = 0;
+ protected float mYOffset = 0;
+
+ protected enum MODES {
+ NONE, DOWN, UP, MOVE
+ }
+
+ protected MODES mMode = MODES.NONE;
+
+ private static final String LOGTAG = "ImageGeometry";
+
+ public ImageGeometry(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageGeometry(Context context) {
+ super(context);
+ }
+
+ private void setupLocalDisplayBounds(RectF b) {
+ mLocalDisplayBounds = b;
+ calculateLocalScalingFactorAndOffset();
+ }
+
+ private void calculateLocalScalingFactorAndOffset() {
+ if (mLocalGeoMetadata == null || mLocalDisplayBounds == null)
+ return;
+ RectF imageBounds = mLocalGeoMetadata.getPhotoBounds();
+ float imageWidth = imageBounds.width();
+ float imageHeight = imageBounds.height();
+ float displayWidth = mLocalDisplayBounds.width();
+ float displayHeight = mLocalDisplayBounds.height();
+
+ mCenterX = displayWidth / 2;
+ mCenterY = displayHeight / 2;
+ float zoom = displayWidth / imageWidth;
+ if (imageHeight > imageWidth) {
+ zoom = displayHeight / imageHeight;
+ }
+ mYOffset = (displayHeight - imageHeight) / 2.0f;
+ mXOffset = (displayWidth - imageWidth) / 2.0f;
+ mLocalGeoMetadata.setScaleFactor(zoom);
+ }
+
+ @Override
+ public void resetParameter() {
+ super.resetParameter();
+ setLocalRotation(0);
+ setLocalStraighten(0);
+ setLocalCropBounds(getLocalPhotoBounds());
+ setLocalFlip(FLIP.NONE);
+ saveAndSetPreset();
+ invalidate();
+ }
+
+ private GeometryMetadata getMasterImageGeometryMetadataCopy() {
+ return getMaster().getGeometry();
+ }
+
+ // Overwrites local with master
+ protected void syncLocalToMasterGeometry() {
+ mLocalGeoMetadata = getMasterImageGeometryMetadataCopy();
+ calculateLocalScalingFactorAndOffset();
+ }
+
+ protected RectF getLocalPhotoBounds() {
+ return mLocalGeoMetadata.getPhotoBounds();
+ }
+
+ protected RectF getLocalCropBounds() {
+ return mLocalGeoMetadata.getCropBounds();
+ }
+
+ protected RectF getLocalDisplayBounds() {
+ return new RectF(mLocalDisplayBounds);
+ }
+
+ protected float getLocalScale() {
+ return mLocalGeoMetadata.getScaleFactor();
+ }
+
+ protected float getLocalRotation() {
+ return mLocalGeoMetadata.getRotation();
+ }
+
+ protected float getLocalStraighten() {
+ return mLocalGeoMetadata.getStraightenRotation();
+ }
+
+ protected void setLocalScale(float s) {
+ mLocalGeoMetadata.setScaleFactor(s);
+ }
+
+ protected void setLocalRotation(float r) {
+ mLocalGeoMetadata.setRotation(r);
+ }
+
+ protected void setLocalStraighten(float r) {
+ mLocalGeoMetadata.setStraightenRotation(r);
+ }
+
+ protected void setLocalCropBounds(RectF c) {
+ mLocalGeoMetadata.setCropBounds(c);
+ }
+
+ protected FLIP getLocalFlip() {
+ return mLocalGeoMetadata.getFlipType();
+ }
+
+ protected void setLocalFlip(FLIP flip) {
+ mLocalGeoMetadata.setFlipType(flip);
+ }
+
+ protected float getTotalLocalRotation() {
+ return getLocalRotation() + getLocalStraighten();
+ }
+
+ protected Bitmap getMasterImage() {
+ if (getMaster() == null)
+ return null;
+ return getMaster().mForegroundImage;
+ }
+
+ protected static Matrix getHorizontalMatrix(int width) {
+ Matrix flipHorizontalMatrix = new Matrix();
+ flipHorizontalMatrix.setScale(-1, 1);
+ flipHorizontalMatrix.postTranslate(width, 0);
+ return flipHorizontalMatrix;
+ }
+
+ protected static Matrix getVerticalMatrix(int height) {
+ Matrix flipVerticalMatrix = new Matrix();
+ flipVerticalMatrix.setScale(1, -1);
+ flipVerticalMatrix.postTranslate(0, height);
+ return flipVerticalMatrix;
+ }
+
+ protected static Matrix getFlipMatrix(FLIP type, int width, int height) {
+ if (type == FLIP.HORIZONTAL) {
+ return getHorizontalMatrix(width);
+ } else if (type == FLIP.VERTICAL) {
+ return getVerticalMatrix(height);
+ } else if (type == FLIP.BOTH) {
+ Matrix flipper = getVerticalMatrix(height);
+ flipper.postConcat(getHorizontalMatrix(width));
+ return flipper;
+ } else {
+ Matrix m = new Matrix();
+ m.reset(); // identity
+ return m;
+ }
+ }
+
+ protected static float clamp(float i, float low, float high) {
+ return Math.max(Math.min(i, high), low);
+ }
+
+ protected static float[] getCornersFromRect(RectF r) {
+ // Order is:
+ // 0------->1
+ // ^ |
+ // | v
+ // 3<-------2
+ float[] corners = {
+ r.left, r.top, // 0
+ r.right, r.top, // 1
+ r.right, r.bottom,// 2
+ r.left, r.bottom
+ };// 3
+ return corners;
+ }
+
+ // Returns maximal rectangular crop bound that still fits within
+ // the image bound after the image has been rotated.
+ protected static RectF findCropBoundForRotatedImg(RectF cropBound,
+ RectF imageBound,
+ float rotation,
+ float centerX,
+ float centerY) {
+ Matrix m = new Matrix();
+ float[] cropEdges = getCornersFromRect(cropBound);
+ m.setRotate(rotation, centerX, centerY);
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0))
+ return null;
+ m0.mapPoints(cropEdges);
+ getEdgePoints(imageBound, cropEdges);
+ m.mapPoints(cropEdges);
+ return trapToRect(cropEdges);
+ }
+
+ // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
+ // image bound rectangle, clamps it to the edge of the rectangle.
+ protected static void getEdgePoints(RectF imageBound, float[] array) {
+ if (array.length < 2)
+ return;
+ for (int x = 0; x < array.length; x += 2) {
+ array[x] = clamp(array[x], imageBound.left, imageBound.right);
+ array[x + 1] = clamp(array[x + 1], imageBound.top, imageBound.bottom);
+ }
+ }
+
+ protected static RectF trapToRect(float[] array) {
+ float dx0 = array[4] - array[0];
+ float dy0 = array[5] - array[1];
+ float dx1 = array[6] - array[2];
+ float dy1 = array[7] - array[3];
+ float l0 = dx0 * dx0 + dy0 * dy0;
+ float l1 = dx1 * dx1 + dy1 * dy1;
+ if (l0 > l1) {
+ RectF n = new RectF(array[2], array[3], array[6], array[7]);
+ n.sort();
+ return n;
+ } else {
+ RectF n = new RectF(array[0], array[1], array[4], array[5]);
+ n.sort();
+ return n;
+ }
+ }
+
+ protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
+ Path crop = new Path();
+ crop.moveTo(points[0], points[1]);
+ crop.lineTo(points[2], points[3]);
+ crop.lineTo(points[4], points[5]);
+ crop.lineTo(points[6], points[7]);
+ crop.close();
+ canvas.drawPath(crop, paint);
+ return crop;
+ }
+
+ protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
+ float x1 = l1[0];
+ float x2 = l2[0];
+ float y1 = l1[1];
+ float y2 = l2[1];
+ float xdelt = x2 - x1;
+ float ydelt = y2 - y1;
+ if (xdelt == 0 && ydelt == 0)
+ return null;
+ float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt)
+ / (xdelt * xdelt + ydelt * ydelt);
+ float[] ret = {
+ (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
+ };
+ return ret;
+ }
+
+ protected static void fixAspectRatio(RectF r, float w, float h) {
+ float scale = Math.min(r.width() / w, r.height() / h);
+ r.set(r.left, r.top, scale * w, scale * h);
+ }
+
+ protected static float getNewHeightForWidthAspect(float width, float w, float h) {
+ return width * h / w;
+ }
+
+ protected static float getNewWidthForHeightAspect(float height, float w, float h) {
+ return height * w / h;
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (visibility == View.VISIBLE) {
+ mVisibilityGained = true;
+ syncLocalToMasterGeometry();
+ gainedVisibility();
+ } else {
+ if (mVisibilityGained == true && mHasDrawn == true) {
+ lostVisibility();
+ }
+ mVisibilityGained = false;
+ mHasDrawn = false;
+ }
+ }
+
+ protected void gainedVisibility() {
+ // TODO: Override this stub.
+ }
+
+ protected void lostVisibility() {
+ // TODO: Override this stub.
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ setupLocalDisplayBounds(new RectF(0, 0, w, h));
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case (MotionEvent.ACTION_DOWN):
+ setActionDown(event.getX(), event.getY());
+ break;
+ case (MotionEvent.ACTION_UP):
+ setActionUp();
+ saveAndSetPreset();
+ break;
+ case (MotionEvent.ACTION_MOVE):
+ setActionMove(event.getX(), event.getY());
+ break;
+ default:
+ setNoAction();
+ }
+ if (getPanelController() != null) {
+ getPanelController().onNewValue((int) getLocalValue());
+ }
+ invalidate();
+ return true;
+ }
+
+ protected int getLocalValue() {
+ return 0; // TODO: Override this
+ }
+
+ protected void setActionDown(float x, float y) {
+ mTouchCenterX = x;
+ mTouchCenterY = y;
+ mCurrentX = x;
+ mCurrentY = y;
+ mMode = MODES.DOWN;
+ }
+
+ protected void setActionMove(float x, float y) {
+ mCurrentX = x;
+ mCurrentY = y;
+ mMode = MODES.MOVE;
+ }
+
+ protected void setActionUp() {
+ mMode = MODES.UP;
+ }
+
+ protected void setNoAction() {
+ mMode = MODES.NONE;
+ }
+
+ @Override
+ public boolean showTitle() {
+ return false;
+ }
+
+ protected void saveAndSetPreset() {
+ ImagePreset copy = new ImagePreset(getImagePreset());
+ copy.setGeometry(mLocalGeoMetadata);
+ setImagePreset(copy);
+ }
+
+ @Override
+ public void updateImage() {
+ }
+
+ protected void drawRegularFlippedBitmap(Canvas canvas, Bitmap image, Paint p) {
+ float zoom = getLocalScale();
+ canvas.save();
+ canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY);
+ canvas.scale(zoom, zoom, mCenterX, mCenterY);
+ canvas.translate(mXOffset, mYOffset);
+ Matrix flipper = getFlipMatrix(getLocalFlip(), image.getWidth(), image.getHeight());
+ canvas.drawBitmap(image, flipper, p);
+ canvas.restore();
+ }
+
+ protected void drawRegularBitmap(Canvas canvas, Bitmap image, Paint p) {
+ float zoom = getLocalScale();
+ canvas.save();
+ canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY);
+ canvas.scale(zoom, zoom, mCenterX, mCenterY);
+ canvas.translate(mXOffset, mYOffset);
+ canvas.drawBitmap(image, 0, 0, p);
+ canvas.restore();
+ }
+
+ protected RectF getCropBoundsDisplayed() {
+ return getCropBoundsDisplayed(getLocalCropBounds());
+ }
+
+ protected RectF getCropBoundsDisplayed(RectF bounds) {
+ RectF crop = new RectF(bounds);
+ Matrix m = new Matrix();
+ float zoom = getLocalScale();
+ m.setScale(zoom, zoom, mCenterX, mCenterY);
+ m.preTranslate(mXOffset, mYOffset);
+ m.mapRect(crop);
+ return crop;
+ }
+
+ protected static float[] correctAngles(float rotation, float straighten) {
+ float[] ret = {
+ rotation, straighten
+ };
+ if (straighten >= MIN_STRAIGHTEN_ANGLE && straighten <= MAX_STRAIGHTEN_ANGLE
+ && (rotation % 90 == 0)) {
+ return ret;
+ }
+ float remainder = rotation % 90;
+ float newRot = (int) (rotation / 90);
+ float tempStraighten = straighten + remainder;
+ newRot += (int) (tempStraighten / 90);
+ tempStraighten %= 90;
+ if (tempStraighten > MAX_STRAIGHTEN_ANGLE) {
+ tempStraighten -= 90;
+ newRot += 1;
+ } else if (tempStraighten < MIN_STRAIGHTEN_ANGLE) {
+ tempStraighten += 90;
+ newRot -= 1;
+ }
+ ret[0] = newRot * 90;
+ ret[1] = tempStraighten;
+ return ret;
+ }
+
+ protected void correctStraightenRotateAngles() {
+ float straighten = getLocalStraighten();
+ float rotation = getLocalRotation();
+ float[] angles = correctAngles(rotation, straighten);
+ setLocalStraighten(angles[1]);
+ setLocalRotation(angles[0]);
+ }
+
+ protected static Matrix getCropRotationMatrix(float rotation) {
+ Matrix m = new Matrix();
+ m.setRotate(rotation);
+ if (!m.rectStaysRect()) {
+ return null;
+ }
+ return m;
+ }
+
+ protected RectF getStraightenCropBounds() {
+ RectF imageRect = getLocalPhotoBounds();
+ Matrix m = new Matrix();
+ float lRot = getLocalRotation();
+ m.setRotate(lRot);
+ if (!m.rectStaysRect()) {
+ correctStraightenRotateAngles();
+ m.setRotate(getLocalRotation());
+ }
+ RectF crop = getStraightenCropBounds(imageRect, getLocalStraighten());
+ setLocalCropBounds(crop);
+ if (!m.mapRect(crop)) {
+ return null;
+ }
+ return crop;
+ }
+
+ protected RectF getStraightenCropBounds(RectF imageRect, float straightenAngle) {
+ float deg = straightenAngle;
+ if (deg < 0) {
+ deg = -deg;
+ }
+ double a = Math.toRadians(deg);
+ double sina = Math.sin(a);
+ double cosa = Math.cos(a);
+
+ double rw = imageRect.width();
+ double rh = imageRect.height();
+ double h1 = rh * rh / (rw * sina + rh * cosa);
+ double h2 = rh * rw / (rw * cosa + rh * sina);
+ double hh = Math.min(h1, h2);
+ double ww = hh * rw / rh;
+
+ float left = (float) ((rw - ww) * 0.5f);
+ float top = (float) ((rh - hh) * 0.5f);
+ float right = (float) (left + ww);
+ float bottom = (float) (top + hh);
+
+ RectF boundsRect = new RectF(left, top, right, bottom);
+ RectF nonRotateImage = getLocalPhotoBounds();
+ Matrix m1 = new Matrix();
+ m1.setTranslate(nonRotateImage.centerX() - boundsRect.centerX(), nonRotateImage.centerY()
+ - boundsRect.centerY());
+ m1.mapRect(boundsRect);
+ return boundsRect;
+ }
+
+ protected void drawShadows(Canvas canvas, RectF innerBounds, RectF outerBounds, Paint p) {
+ float dWidth = outerBounds.width();
+ float dHeight = outerBounds.height();
+ canvas.drawRect(0, 0, dWidth, innerBounds.top, p);
+ canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p);
+ canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom,
+ p);
+ canvas.drawRect(innerBounds.right, innerBounds.top, dWidth,
+ innerBounds.bottom, p);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (getDirtyGeometryFlag()) {
+ syncLocalToMasterGeometry();
+ clearDirtyGeometryFlag();
+ }
+ Bitmap image = getMasterImage();
+ if (image == null) {
+ return;
+ }
+ mHasDrawn = true;
+ drawShape(canvas, image);
+ }
+
+ protected void drawShape(Canvas canvas, Bitmap image) {
+ // TODO: Override this stub.
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java
new file mode 100644
index 000000000..c0999e3d0
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java
@@ -0,0 +1,161 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+
+public class ImageRotate extends ImageGeometry {
+ private static final float MATH_PI = (float) Math.PI;
+
+ private float mBaseAngle = 0;
+ private float mAngle = 0;
+
+ private RectF mLocalBoundsCopy = null;
+ private boolean mSnapToNinety = true;
+ private static final String LOGTAG = "ImageRotate";
+
+ public ImageRotate(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageRotate(Context context) {
+ super(context);
+ }
+
+ private float angleFor(float dx, float dy) {
+ return (float) (Math.atan2(dx, dy) * 180 / MATH_PI);
+ }
+
+ private int snappedAngle(float angle) {
+ float remainder = angle % 90;
+ int current = (int) (angle / 90); // truncates
+ if (remainder < -45) {
+ --current;
+ } else if (remainder > 45) {
+ ++current;
+ }
+ return current * 90;
+ }
+
+ private static final Paint gPaint = new Paint();
+
+ private void computeValue() {
+ if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
+ return;
+ }
+ float dX1 = mTouchCenterX - mCenterX;
+ float dY1 = mTouchCenterY - mCenterY;
+ float dX2 = mCurrentX - mCenterX;
+ float dY2 = mCurrentY - mCenterY;
+
+ float angleA = angleFor(dX1, dY1);
+ float angleB = angleFor(dX2, dY2);
+ float angle = (angleB - angleA) % 360;
+ mAngle = (mBaseAngle - angle) % 360;
+ }
+
+ @Override
+ protected void setActionDown(float x, float y) {
+ super.setActionDown(x, y);
+ mBaseAngle = mAngle = getLocalRotation();
+ }
+
+ @Override
+ protected void setActionMove(float x, float y) {
+ super.setActionMove(x, y);
+ computeValue();
+ setLocalRotation(mAngle % 360);
+ }
+
+ @Override
+ protected void setActionUp() {
+ super.setActionUp();
+ if (mSnapToNinety) {
+ setLocalRotation(snappedAngle(mAngle % 360));
+ }
+ }
+
+ @Override
+ public void resetParameter() {
+ super.resetParameter();
+ mLocalBoundsCopy = getLocalCropBounds();
+ }
+
+ @Override
+ protected void gainedVisibility() {
+ mLocalBoundsCopy = getLocalCropBounds();
+ }
+
+ @Override
+ protected void lostVisibility() {
+ }
+
+ @Override
+ protected int getLocalValue() {
+ return (int) getLocalRotation();
+ }
+
+ @Override
+ protected void drawShape(Canvas canvas, Bitmap image) {
+ gPaint.setAntiAlias(true);
+ gPaint.setFilterBitmap(true);
+ gPaint.setDither(true);
+ gPaint.setARGB(255, 255, 255, 255);
+
+ drawRegularFlippedBitmap(canvas, image, gPaint);
+
+ RectF cropBounds = getCropBoundsDisplayed(mLocalBoundsCopy);
+
+ Matrix m0 = new Matrix();
+ m0.setRotate(getLocalRotation(), mCenterX, mCenterY);
+ float[] corners = getCornersFromRect(cropBounds);
+ m0.mapPoints(corners);
+
+ gPaint.setARGB(255, 255, 255, 255);
+ gPaint.setStrokeWidth(3);
+ gPaint.setStyle(Paint.Style.STROKE);
+ drawClosedPath(canvas, gPaint, corners);
+
+ canvas.save();
+ canvas.rotate(getLocalRotation(), mCenterX, mCenterY);
+ RectF displayRect = getLocalDisplayBounds();
+ float dWidth = displayRect.width();
+ float dHeight = displayRect.height();
+ RectF boundsRect = cropBounds;
+ gPaint.setARGB(128, 0, 0, 0);
+ // TODO: move style to xml
+ gPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint);
+ canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint);
+ canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom,
+ gPaint);
+ canvas.drawRect(boundsRect.right, boundsRect.top, dWidth,
+ boundsRect.bottom, gPaint);
+ canvas.rotate(-getLocalRotation(), mCenterX, mCenterY);
+ canvas.restore();
+
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index da38d3b7e..d6d3793ce 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -1,3 +1,18 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
@@ -6,6 +21,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -38,6 +54,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
protected ImagePreset mImagePreset = null;
protected ImageLoader mImageLoader = null;
private ImageFilter mCurrentFilter = null;
+ private boolean mDirtyGeometry = true;
private Bitmap mBackgroundImage = null;
protected Bitmap mForegroundImage = null;
@@ -48,9 +65,14 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
private HistoryAdapter mHistoryAdapter = null;
private ImageStateAdapter mImageStateAdapter = null;
- protected Rect mImageBounds = null;
- protected float mImageRotation = 0;
- protected float mImageRotationZoomFactor = 0;
+ protected GeometryMetadata getGeometry(){
+ return new GeometryMetadata(mImagePreset.mGeoData);
+ }
+
+ public void setGeometry(GeometryMetadata d){
+ mImagePreset.mGeoData.set(d);
+ }
+
private boolean mShowControls = false;
private boolean mShowOriginal = false;
@@ -190,7 +212,9 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
}
public Rect getImageBounds() {
- return mImageBounds;
+ Rect dst = new Rect();
+ mImagePreset.mGeoData.getPhotoBounds().roundOut(dst);
+ return dst;
}
public ImagePreset getImagePreset() {
@@ -284,7 +308,6 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
float h = w / ratio;
float ty = (getHeight() - h) / 2.0f;
float tx = 0;
- // t = 0;
if (ratio < 1.0f) { // portrait image
h = getHeight();
w = h * ratio;
@@ -293,7 +316,6 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
}
Rect d = new Rect((int) tx, (int) ty, (int) (w + tx),
(int) (h + ty));
- mImageBounds = d;
canvas.drawBitmap(image, s, d, mPaint);
}
@@ -361,18 +383,33 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
}
}
+ protected void setDirtyGeometryFlag(){
+ mDirtyGeometry = true;
+ }
+
+ protected void clearDirtyGeometryFlag(){
+ mDirtyGeometry = false;
+ }
+
+ protected boolean getDirtyGeometryFlag() {
+ return mDirtyGeometry;
+ }
+
+ private void imageSizeChanged(Bitmap image) {
+ if(image == null || mImagePreset == null)
+ return;
+ float w = image.getWidth();
+ float h = image.getHeight();
+ RectF r = new RectF(0, 0, w, h);
+ RectF c = new RectF(w/4f, h/4f, w * 3/4f, h * 3/4f);
+ mImagePreset.mGeoData.setPhotoBounds(r);
+ mImagePreset.mGeoData.setCropBounds(c);
+ setDirtyGeometryFlag();
+ }
+
public void updateImage() {
mForegroundImage = getOriginalFrontBitmap();
- /*
- * if (mImageLoader != null) {
- * mImageLoader.resetImageForPreset(getImagePreset(), this); }
- */
-
- /*
- * if (mForegroundImage != null) { Bitmap filteredImage =
- * mForegroundImage.copy(mConfig, true);
- * getImagePreset().apply(filteredImage); invalidate(); }
- */
+ imageSizeChanged(mForegroundImage); // TODO: should change to filtered
}
public void updateFilteredImage(Bitmap bitmap) {
@@ -413,20 +450,29 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
}
public float getImageRotation() {
- return mImageRotation;
+ return mImagePreset.mGeoData.getRotation();
}
public float getImageRotationZoomFactor() {
- return mImageRotationZoomFactor;
+ return mImagePreset.mGeoData.getScaleFactor();
+ }
+
+ public void setImageRotation(float r){
+ mImagePreset.mGeoData.setRotation(r);
+ }
+
+ public void setImageRotationZoomFactor(float f){
+ mImagePreset.mGeoData.setScaleFactor(f);
}
public void setImageRotation(float imageRotation,
float imageRotationZoomFactor) {
- if (imageRotation != mImageRotation) {
+ float r = getImageRotation();
+ if (imageRotation != r) {
invalidate();
}
- mImageRotation = imageRotation;
- mImageRotationZoomFactor = imageRotationZoomFactor;
+ setImageRotation(imageRotation);
+ setImageRotationZoomFactor(imageRotationZoomFactor);
}
@Override
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSlave.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSlave.java
index 4fdf8303a..f320a0341 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSlave.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSlave.java
@@ -1,3 +1,19 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
import android.content.Context;
@@ -47,10 +63,6 @@ public class ImageSlave extends ImageShow {
return mMasterImageShow.getCurrentFilter();
}
- public void updateAngle() {
- mMasterImageShow.setImageRotation(mImageRotation, mImageRotationZoomFactor);
- }
-
@Override
public boolean showTitle() {
return false;
@@ -81,5 +93,4 @@ public class ImageSlave extends ImageShow {
return mMasterImageShow.getPanelController();
}
-
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index 99aa389ac..2b15bf373 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -1,3 +1,18 @@
+/*
+ * 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.gallery3d.filtershow.imageshow;
@@ -13,26 +28,10 @@ import android.view.MotionEvent;
import com.android.gallery3d.filtershow.presets.ImagePreset;
-public class ImageStraighten extends ImageSlave {
- private float mImageRotation = 0;
- private float mImageRotationZoomFactor = 0;
+public class ImageStraighten extends ImageGeometry {
- private final float mMinAngle = -45;
- private final float mMaxAngle = 45;
private float mBaseAngle = 0;
private float mAngle = 0;
- private float mCenterX;
- private float mCenterY;
- private float mTouchCenterX;
- private float mTouchCenterY;
- private float mCurrentX;
- private float mCurrentY;
-
- private enum MODES {
- NONE, DOWN, UP, MOVE
- }
-
- private MODES mMode = MODES.NONE;
private static final String LOGTAG = "ImageStraighten";
private static final Paint gPaint = new Paint();
@@ -45,72 +44,17 @@ public class ImageStraighten extends ImageSlave {
super(context, attrs);
}
- // ///////////////////////////////////////////////////////////////////////////
- // touch event handler
-
- public void setActionDown(float x, float y) {
- mTouchCenterX = x;
- mTouchCenterY = y;
- mCurrentX = x;
- mCurrentY = y;
- mBaseAngle = mAngle;
- mMode = MODES.DOWN;
- }
-
- public void setActionMove(float x, float y) {
- mCurrentX = x;
- mCurrentY = y;
- mMode = MODES.MOVE;
- computeValue();
- }
-
- public void setActionUp() {
- mMode = MODES.UP;
- updatePreset();
- invalidate();
- }
-
- public void setNoAction() {
- mMode = MODES.NONE;
- }
-
- private void updatePreset() {
- ImagePreset copy = new ImagePreset(getImagePreset());
- copy.setStraightenRotation(mImageRotation, mImageRotationZoomFactor);
- setImagePreset(copy);
- }
-
@Override
- public void resetParameter() {
- super.resetParameter();
- mImageRotation = 0;
- mAngle = 0;
- updatePreset();
- invalidate();
+ protected void setActionDown(float x, float y) {
+ super.setActionDown(x, y);
+ mBaseAngle = mAngle = getLocalStraighten();
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getActionMasked()) {
- case (MotionEvent.ACTION_DOWN):
- setActionDown(event.getX(), event.getY());
- break;
- case (MotionEvent.ACTION_UP):
- setActionUp();
- break;
- case (MotionEvent.ACTION_MOVE):
- setActionMove(event.getX(), event.getY());
- break;
- default:
- setNoAction();
- }
- mImageRotation = mAngle;
- updateAngle();
- if (getPanelController() != null) {
- getPanelController().onNewValue((int) mImageRotation);
- }
- invalidate();
- return true;
+ protected void setActionMove(float x, float y) {
+ super.setActionMove(x, y);
+ computeValue();
+ setLocalStraighten(mAngle);
}
private float angleFor(float dx, float dy) {
@@ -130,122 +74,91 @@ public class ImageStraighten extends ImageSlave {
float angleB = angleFor(dX2, dY2);
float angle = (angleB - angleA) % 360;
mAngle = (mBaseAngle - angle) % 360;
- mAngle = Math.max(mMinAngle, mAngle);
- mAngle = Math.min(mMaxAngle, mAngle);
+ mAngle = Math.max(MIN_STRAIGHTEN_ANGLE, mAngle);
+ mAngle = Math.min(MAX_STRAIGHTEN_ANGLE, mAngle);
}
- // ///////////////////////////////////////////////////////////////////////////
+ @Override
+ protected void gainedVisibility() {
+ correctStraightenRotateAngles();
+ }
+
+ @Override
+ protected void lostVisibility() {
+ saveAndSetPreset();
+ }
@Override
public void onNewValue(int value) {
- mImageRotation = value;
+ setLocalStraighten(clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE));
if (getPanelController() != null) {
- getPanelController().onNewValue(value);
+ getPanelController().onNewValue((int) getLocalStraighten());
}
invalidate();
}
@Override
- public void onDraw(Canvas canvas) {
- mCenterX = getWidth() / 2;
- mCenterY = getHeight() / 2;
- drawStraighten(canvas);
+ protected int getLocalValue() {
+ return (int) getLocalStraighten();
}
- public void drawStraighten(Canvas canvas) {
+ @Override
+ protected void drawShape(Canvas canvas, Bitmap image) {
gPaint.setAntiAlias(true);
gPaint.setFilterBitmap(true);
gPaint.setDither(true);
gPaint.setARGB(255, 255, 255, 255);
- // canvas.drawARGB(255, 255, 0, 0);
+ // Draw fully rotated image.
+ drawRegularFlippedBitmap(canvas, image, gPaint);
- // TODO: have the concept of multiple image passes (geometry, filter)
- // so that we can fake the rotation, etc.
- Bitmap image = null; // mMasterImageShow.mFilteredImage;
- if (image == null) {
- image = getMaster().mForegroundImage;
- }
- if (image == null) {
- return;
- }
+ // Get cropping frame
+ RectF boundsRect = getStraightenCropBounds();
- double iw = image.getWidth();
- float zoom = (float) (getWidth() / iw);
- // iw = getWidth(); // we will apply the zoom
- double ih = image.getHeight();
- if (ih > iw) {
- zoom = (float) (getHeight() / ih);
- }
- float offset = (float) ((getHeight() - ih) / 2.0f);
-
- canvas.save();
- float dx = (float) ((getWidth() - iw) / 2.0f);
- float dy = offset;
-
- canvas.rotate(mImageRotation, mCenterX, mCenterY);
- canvas.scale(zoom, zoom, mCenterX, mCenterY);
- canvas.translate(dx, dy);
- canvas.drawBitmap(image, 0, 0, gPaint);
-
- canvas.restore();
-
- double deg = mImageRotation;
- if (deg < 0) {
- deg = -deg;
- }
- double a = Math.toRadians(deg);
- double sina = Math.sin(a);
- double cosa = Math.cos(a);
-
- double rw = image.getWidth();
- double rh = image.getHeight();
- double h1 = rh * rh / (rw * sina + rh * cosa);
- double h2 = rh * rw / (rw * cosa + rh * sina);
- double hh = Math.min(h1, h2);
- double ww = hh * rw / rh;
-
- float left = (float) ((rw - ww) * 0.5f);
- float top = (float) ((rh - hh) * 0.5f);
- float right = (float) (left + ww);
- float bottom = (float) (top + hh);
-
- RectF boundsRect = new RectF(left, top, right, bottom);
- Matrix m = new Matrix();
- m.setScale(zoom, zoom, mCenterX, mCenterY);
- m.preTranslate(dx, dy);
- m.mapRect(boundsRect);
+ Matrix m1 = new Matrix();
+ float zoom = getLocalScale();
+ // Center and scale
+ m1.setScale(zoom, zoom, mCenterX, mCenterY);
+ m1.preTranslate(mCenterX - boundsRect.centerX(), mCenterY - boundsRect.centerY());
+ m1.mapRect(boundsRect);
+ RectF displayRect = getLocalDisplayBounds();
+ float dWidth = displayRect.width();
+ float dHeight = displayRect.height();
+ // Draw shadows
gPaint.setARGB(128, 0, 0, 0);
gPaint.setStyle(Paint.Style.FILL);
- canvas.drawRect(0, 0, getWidth(), boundsRect.top, gPaint);
- canvas.drawRect(0, boundsRect.bottom, getWidth(), getHeight(), gPaint);
+
+ // TODO: move to xml file
+ canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint);
+ canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint);
canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom,
gPaint);
- canvas.drawRect(boundsRect.right, boundsRect.top, getWidth(),
+ canvas.drawRect(boundsRect.right, boundsRect.top, dWidth,
boundsRect.bottom, gPaint);
+ // Draw crop frame
Path path = new Path();
path.addRect(boundsRect, Path.Direction.CCW);
gPaint.setARGB(255, 255, 255, 255);
gPaint.setStrokeWidth(3);
gPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, gPaint);
- gPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- mImageRotationZoomFactor = (float) (rw / boundsRect.width());
+ gPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ // Draw grid
if (mMode == MODES.MOVE) {
canvas.save();
canvas.clipPath(path);
int n = 16;
- float step = getWidth() / n;
+ float step = dWidth / n;
float p = 0;
for (int i = 1; i < n; i++) {
p = i * step;
gPaint.setARGB(60, 255, 255, 255);
- canvas.drawLine(p, 0, p, getHeight(), gPaint);
- canvas.drawLine(0, p, getWidth(), p, gPaint);
+ canvas.drawLine(p, 0, p, dHeight, gPaint);
+ canvas.drawLine(0, p, dWidth, p, gPaint);
}
canvas.restore();
}
diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
index c0c310374..be08e7090 100644
--- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
@@ -8,6 +8,7 @@ import android.util.Log;
import com.android.gallery3d.filtershow.ImageStateAdapter;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterStraighten;
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import java.util.Vector;
@@ -22,15 +23,15 @@ public class ImagePreset {
protected boolean mIsFxPreset = false;
enum FullRotate {
- ZERO, NINETY, HUNDRED_HEIGHTY, TWO_HUNDRED_SEVENTY
+ ZERO, NINETY, HUNDRED_EIGHTY, TWO_HUNDRED_SEVENTY
}
- protected FullRotate mFullRotate = FullRotate.ZERO;
- protected float mStraightenRotate = 0;
- protected float mStraightenZoom = 0;
- protected boolean mHorizontalFlip = false;
- protected boolean mVerticalFlip = false;
- protected RectF mCrop = null;
+ // This is where the geometry metadata lives now.
+ public final GeometryMetadata mGeoData = new GeometryMetadata();
+
+ public void setGeometry(GeometryMetadata m) {
+ mGeoData.set(m);
+ }
private float mScaleFactor = 1.0f;
private boolean mIsHighQuality = false;
@@ -51,32 +52,13 @@ public class ImagePreset {
mHistoryName = source.name();
mIsFxPreset = source.isFx();
- mStraightenRotate = source.mStraightenRotate;
- mStraightenZoom = source.mStraightenZoom;
-
- mScaleFactor = source.mScaleFactor;
- mIsHighQuality = source.mIsHighQuality;
- }
-
- public void setStraightenRotation(float rotate, float zoom) {
- mStraightenRotate = rotate;
- mStraightenZoom = zoom;
+ mGeoData.set(source.mGeoData);
}
private Bitmap applyGeometry(Bitmap original, float scaleFactor, boolean highQuality) {
Bitmap bitmap = original;
- if (mFullRotate != FullRotate.ZERO) {
- // TODO
- }
-
- if (mStraightenRotate != 0) {
- // TODO: keep the instances around
- ImageFilter straighten = new ImageFilterStraighten(mStraightenRotate, mStraightenZoom);
- bitmap = straighten.apply(bitmap, scaleFactor, highQuality);
- straighten = null;
- }
-
+ // TODO: put geometry filters
return bitmap;
}
@@ -104,7 +86,8 @@ public class ImagePreset {
if (!mName.equalsIgnoreCase(preset.name())) {
return false;
}
- if (mStraightenRotate != preset.mStraightenRotate) {
+
+ if (!mGeoData.equals(preset.mGeoData)) {
return false;
}
for (int i = 0; i < preset.mFilters.size(); i++) {