summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/filtershow/imageshow/ImageShow.java')
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java578
1 files changed, 578 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
new file mode 100644
index 000000000..6278b2ad4
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -0,0 +1,578 @@
+/*
+ * 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.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.filters.ImageFilter;
+import com.android.gallery3d.filtershow.pipeline.ImagePreset;
+import com.android.gallery3d.filtershow.tools.SaveImage;
+
+import java.io.File;
+
+public class ImageShow extends View implements OnGestureListener,
+ ScaleGestureDetector.OnScaleGestureListener,
+ OnDoubleTapListener {
+
+ private static final String LOGTAG = "ImageShow";
+ private static final boolean ENABLE_ZOOMED_COMPARISON = false;
+
+ protected Paint mPaint = new Paint();
+ protected int mTextSize;
+ protected int mTextPadding;
+
+ protected int mBackgroundColor;
+
+ private GestureDetector mGestureDetector = null;
+ private ScaleGestureDetector mScaleGestureDetector = null;
+
+ protected Rect mImageBounds = new Rect();
+ private boolean mOriginalDisabled = false;
+ private boolean mTouchShowOriginal = false;
+ private long mTouchShowOriginalDate = 0;
+ private final long mTouchShowOriginalDelayMin = 200; // 200ms
+ private int mShowOriginalDirection = 0;
+ private static int UNVEIL_HORIZONTAL = 1;
+ private static int UNVEIL_VERTICAL = 2;
+
+ private Point mTouchDown = new Point();
+ private Point mTouch = new Point();
+ private boolean mFinishedScalingOperation = false;
+
+ private int mOriginalTextMargin;
+ private int mOriginalTextSize;
+ private String mOriginalText;
+ private boolean mZoomIn = false;
+ Point mOriginalTranslation = new Point();
+ float mOriginalScale;
+ float mStartFocusX, mStartFocusY;
+ private enum InteractionMode {
+ NONE,
+ SCALE,
+ MOVE
+ }
+ InteractionMode mInteractionMode = InteractionMode.NONE;
+
+ private FilterShowActivity mActivity = null;
+
+ public FilterShowActivity getActivity() {
+ return mActivity;
+ }
+
+ public boolean hasModifications() {
+ return MasterImage.getImage().hasModifications();
+ }
+
+ public void resetParameter() {
+ // TODO: implement reset
+ }
+
+ public void onNewValue(int parameter) {
+ invalidate();
+ }
+
+ public ImageShow(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setupImageShow(context);
+ }
+
+ public ImageShow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setupImageShow(context);
+
+ }
+
+ public ImageShow(Context context) {
+ super(context);
+ setupImageShow(context);
+ }
+
+ private void setupImageShow(Context context) {
+ Resources res = context.getResources();
+ mTextSize = res.getDimensionPixelSize(R.dimen.photoeditor_text_size);
+ mTextPadding = res.getDimensionPixelSize(R.dimen.photoeditor_text_padding);
+ mOriginalTextMargin = res.getDimensionPixelSize(R.dimen.photoeditor_original_text_margin);
+ mOriginalTextSize = res.getDimensionPixelSize(R.dimen.photoeditor_original_text_size);
+ mBackgroundColor = res.getColor(R.color.background_screen);
+ mOriginalText = res.getString(R.string.original_picture_text);
+ setupGestureDetector(context);
+ mActivity = (FilterShowActivity) context;
+ MasterImage.getImage().addObserver(this);
+ }
+
+ public void setupGestureDetector(Context context) {
+ mGestureDetector = new GestureDetector(context, this);
+ mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(parentWidth, parentHeight);
+ }
+
+ public ImageFilter getCurrentFilter() {
+ return MasterImage.getImage().getCurrentFilter();
+ }
+
+ /* consider moving the following 2 methods into a subclass */
+ /**
+ * This function calculates a Image to Screen Transformation matrix
+ *
+ * @param reflectRotation set true if you want the rotation encoded
+ * @return Image to Screen transformation matrix
+ */
+ protected Matrix getImageToScreenMatrix(boolean reflectRotation) {
+ MasterImage master = MasterImage.getImage();
+ if (master.getOriginalBounds() == null) {
+ return new Matrix();
+ }
+ Matrix m = GeometryMathUtils.getImageToScreenMatrix(master.getPreset().getGeometryFilters(),
+ reflectRotation, master.getOriginalBounds(), getWidth(), getHeight());
+ Point translate = master.getTranslation();
+ float scaleFactor = master.getScaleFactor();
+ m.postTranslate(translate.x, translate.y);
+ m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f);
+ return m;
+ }
+
+ /**
+ * This function calculates a to Screen Image Transformation matrix
+ *
+ * @param reflectRotation set true if you want the rotation encoded
+ * @return Screen to Image transformation matrix
+ */
+ protected Matrix getScreenToImageMatrix(boolean reflectRotation) {
+ Matrix m = getImageToScreenMatrix(reflectRotation);
+ Matrix invert = new Matrix();
+ m.invert(invert);
+ return invert;
+ }
+
+ public ImagePreset getImagePreset() {
+ return MasterImage.getImage().getPreset();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ MasterImage.getImage().setImageShowSize(getWidth(), getHeight());
+
+ float cx = canvas.getWidth()/2.0f;
+ float cy = canvas.getHeight()/2.0f;
+ float scaleFactor = MasterImage.getImage().getScaleFactor();
+ Point translation = MasterImage.getImage().getTranslation();
+
+ Matrix scalingMatrix = new Matrix();
+ scalingMatrix.postScale(scaleFactor, scaleFactor, cx, cy);
+ scalingMatrix.preTranslate(translation.x, translation.y);
+
+ RectF unscaledClipRect = new RectF(mImageBounds);
+ scalingMatrix.mapRect(unscaledClipRect, unscaledClipRect);
+
+ canvas.save();
+
+ boolean enablePartialRendering = false;
+
+ // For now, partial rendering is disabled for all filters,
+ // so no need to clip.
+ if (enablePartialRendering && !unscaledClipRect.isEmpty()) {
+ canvas.clipRect(unscaledClipRect);
+ }
+
+ canvas.save();
+ // TODO: center scale on gesture
+ canvas.scale(scaleFactor, scaleFactor, cx, cy);
+ canvas.translate(translation.x, translation.y);
+ drawImage(canvas, getFilteredImage(), true);
+ Bitmap highresPreview = MasterImage.getImage().getHighresImage();
+ if (highresPreview != null) {
+ drawImage(canvas, highresPreview, true);
+ }
+ canvas.restore();
+
+ Bitmap partialPreview = MasterImage.getImage().getPartialImage();
+ if (partialPreview != null) {
+ Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight());
+ Rect dest = new Rect(0, 0, getWidth(), getHeight());
+ canvas.drawBitmap(partialPreview, src, dest, mPaint);
+ }
+
+ canvas.save();
+ canvas.scale(scaleFactor, scaleFactor, cx, cy);
+ canvas.translate(translation.x, translation.y);
+ drawPartialImage(canvas, getGeometryOnlyImage());
+ canvas.restore();
+
+ canvas.restore();
+ }
+
+ public void resetImageCaches(ImageShow caller) {
+ MasterImage.getImage().updatePresets(true);
+ }
+
+ public Bitmap getFiltersOnlyImage() {
+ return MasterImage.getImage().getFiltersOnlyImage();
+ }
+
+ public Bitmap getGeometryOnlyImage() {
+ return MasterImage.getImage().getGeometryOnlyImage();
+ }
+
+ public Bitmap getFilteredImage() {
+ return MasterImage.getImage().getFilteredImage();
+ }
+
+ public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) {
+ if (image != null) {
+ Rect s = new Rect(0, 0, image.getWidth(),
+ image.getHeight());
+
+ float scale = GeometryMathUtils.scale(image.getWidth(), image.getHeight(), getWidth(),
+ getHeight());
+
+ float w = image.getWidth() * scale;
+ float h = image.getHeight() * scale;
+ float ty = (getHeight() - h) / 2.0f;
+ float tx = (getWidth() - w) / 2.0f;
+
+ Rect d = new Rect((int) tx, (int) ty, (int) (w + tx),
+ (int) (h + ty));
+ if (updateBounds) {
+ mImageBounds = d;
+ }
+ canvas.drawBitmap(image, s, d, mPaint);
+ }
+ }
+
+ public void drawPartialImage(Canvas canvas, Bitmap image) {
+ boolean showsOriginal = MasterImage.getImage().showsOriginal();
+ if (!showsOriginal && !mTouchShowOriginal)
+ return;
+ canvas.save();
+ if (image != null) {
+ if (mShowOriginalDirection == 0) {
+ if (Math.abs(mTouch.y - mTouchDown.y) > Math.abs(mTouch.x - mTouchDown.x)) {
+ mShowOriginalDirection = UNVEIL_VERTICAL;
+ } else {
+ mShowOriginalDirection = UNVEIL_HORIZONTAL;
+ }
+ }
+
+ int px = 0;
+ int py = 0;
+ if (mShowOriginalDirection == UNVEIL_VERTICAL) {
+ px = mImageBounds.width();
+ py = mTouch.y - mImageBounds.top;
+ } else {
+ px = mTouch.x - mImageBounds.left;
+ py = mImageBounds.height();
+ if (showsOriginal) {
+ px = mImageBounds.width();
+ }
+ }
+
+ Rect d = new Rect(mImageBounds.left, mImageBounds.top,
+ mImageBounds.left + px, mImageBounds.top + py);
+ canvas.clipRect(d);
+ drawImage(canvas, image, false);
+ Paint paint = new Paint();
+ paint.setColor(Color.BLACK);
+ paint.setStrokeWidth(3);
+
+ if (mShowOriginalDirection == UNVEIL_VERTICAL) {
+ canvas.drawLine(mImageBounds.left, mTouch.y,
+ mImageBounds.right, mTouch.y, paint);
+ } else {
+ canvas.drawLine(mTouch.x, mImageBounds.top,
+ mTouch.x, mImageBounds.bottom, paint);
+ }
+
+ Rect bounds = new Rect();
+ paint.setAntiAlias(true);
+ paint.setTextSize(mOriginalTextSize);
+ paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds);
+ paint.setColor(Color.BLACK);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(3);
+ canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
+ mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setStrokeWidth(1);
+ paint.setColor(Color.WHITE);
+ canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
+ mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
+ }
+ canvas.restore();
+ }
+
+ public void bindAsImageLoadListener() {
+ MasterImage.getImage().addListener(this);
+ }
+
+ public void updateImage() {
+ invalidate();
+ }
+
+ public void imageLoaded() {
+ updateImage();
+ }
+
+ public void saveImage(FilterShowActivity filterShowActivity, File file) {
+ SaveImage.saveImage(getImagePreset(), filterShowActivity, file);
+ }
+
+
+ public boolean scaleInProgress() {
+ return mScaleGestureDetector.isInProgress();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ int action = event.getAction();
+ action = action & MotionEvent.ACTION_MASK;
+
+ mGestureDetector.onTouchEvent(event);
+ boolean scaleInProgress = scaleInProgress();
+ mScaleGestureDetector.onTouchEvent(event);
+ if (mInteractionMode == InteractionMode.SCALE) {
+ return true;
+ }
+ if (!scaleInProgress() && scaleInProgress) {
+ // If we were scaling, the scale will stop but we will
+ // still issue an ACTION_UP. Let the subclasses know.
+ mFinishedScalingOperation = true;
+ }
+
+ int ex = (int) event.getX();
+ int ey = (int) event.getY();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mInteractionMode = InteractionMode.MOVE;
+ mTouchDown.x = ex;
+ mTouchDown.y = ey;
+ mTouchShowOriginalDate = System.currentTimeMillis();
+ mShowOriginalDirection = 0;
+ MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation());
+ }
+
+ if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) {
+ mTouch.x = ex;
+ mTouch.y = ey;
+
+ float scaleFactor = MasterImage.getImage().getScaleFactor();
+ if (scaleFactor > 1 && (!ENABLE_ZOOMED_COMPARISON || event.getPointerCount() == 2)) {
+ float translateX = (mTouch.x - mTouchDown.x) / scaleFactor;
+ float translateY = (mTouch.y - mTouchDown.y) / scaleFactor;
+ Point originalTranslation = MasterImage.getImage().getOriginalTranslation();
+ Point translation = MasterImage.getImage().getTranslation();
+ translation.x = (int) (originalTranslation.x + translateX);
+ translation.y = (int) (originalTranslation.y + translateY);
+ constrainTranslation(translation, scaleFactor);
+ MasterImage.getImage().setTranslation(translation);
+ mTouchShowOriginal = false;
+ } else if (enableComparison() && !mOriginalDisabled
+ && (System.currentTimeMillis() - mTouchShowOriginalDate
+ > mTouchShowOriginalDelayMin)
+ && event.getPointerCount() == 1) {
+ mTouchShowOriginal = true;
+ }
+ }
+
+ if (action == MotionEvent.ACTION_UP) {
+ mInteractionMode = InteractionMode.NONE;
+ mTouchShowOriginal = false;
+ mTouchDown.x = 0;
+ mTouchDown.y = 0;
+ mTouch.x = 0;
+ mTouch.y = 0;
+ if (MasterImage.getImage().getScaleFactor() <= 1) {
+ MasterImage.getImage().setScaleFactor(1);
+ MasterImage.getImage().resetTranslation();
+ }
+ }
+ invalidate();
+ return true;
+ }
+
+ protected boolean enableComparison() {
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent arg0) {
+ mZoomIn = !mZoomIn;
+ float scale = 1.0f;
+ if (mZoomIn) {
+ scale = MasterImage.getImage().getMaxScaleFactor();
+ }
+ if (scale != MasterImage.getImage().getScaleFactor()) {
+ MasterImage.getImage().setScaleFactor(scale);
+ float translateX = (getWidth() / 2 - arg0.getX());
+ float translateY = (getHeight() / 2 - arg0.getY());
+ Point translation = MasterImage.getImage().getTranslation();
+ translation.x = (int) (mOriginalTranslation.x + translateX);
+ translation.y = (int) (mOriginalTranslation.y + translateY);
+ constrainTranslation(translation, scale);
+ MasterImage.getImage().setTranslation(translation);
+ invalidate();
+ }
+ return true;
+ }
+
+ private void constrainTranslation(Point translation, float scale) {
+ float maxTranslationX = getWidth() / scale;
+ float maxTranslationY = getHeight() / scale;
+ if (Math.abs(translation.x) > maxTranslationX) {
+ translation.x = (int) (Math.signum(translation.x) *
+ maxTranslationX);
+ if (Math.abs(translation.y) > maxTranslationY) {
+ translation.y = (int) (Math.signum(translation.y) *
+ maxTranslationY);
+ }
+
+ }
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent arg0) {
+ return false;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent arg0) {
+ return false;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent arg0) {
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
+ if (mActivity == null) {
+ return false;
+ }
+ if (endEvent.getPointerCount() == 2) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent arg0) {
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent arg0) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent arg0) {
+ return false;
+ }
+
+ public boolean useUtilityPanel() {
+ return false;
+ }
+
+ public void openUtilityPanel(final LinearLayout accessoryViewList) {
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ MasterImage img = MasterImage.getImage();
+ float scaleFactor = img.getScaleFactor();
+
+ scaleFactor = scaleFactor * detector.getScaleFactor();
+ if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
+ scaleFactor = MasterImage.getImage().getMaxScaleFactor();
+ }
+ if (scaleFactor < 0.5) {
+ scaleFactor = 0.5f;
+ }
+ MasterImage.getImage().setScaleFactor(scaleFactor);
+ scaleFactor = img.getScaleFactor();
+ float focusx = detector.getFocusX();
+ float focusy = detector.getFocusY();
+ float translateX = (focusx - mStartFocusX) / scaleFactor;
+ float translateY = (focusy - mStartFocusY) / scaleFactor;
+ Point translation = MasterImage.getImage().getTranslation();
+ translation.x = (int) (mOriginalTranslation.x + translateX);
+ translation.y = (int) (mOriginalTranslation.y + translateY);
+ constrainTranslation(translation, scaleFactor);
+ MasterImage.getImage().setTranslation(translation);
+
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ Point pos = MasterImage.getImage().getTranslation();
+ mOriginalTranslation.x = pos.x;
+ mOriginalTranslation.y = pos.y;
+ mOriginalScale = MasterImage.getImage().getScaleFactor();
+ mStartFocusX = detector.getFocusX();
+ mStartFocusY = detector.getFocusY();
+ mInteractionMode = InteractionMode.SCALE;
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ mInteractionMode = InteractionMode.NONE;
+ if (MasterImage.getImage().getScaleFactor() < 1) {
+ MasterImage.getImage().setScaleFactor(1);
+ invalidate();
+ }
+ }
+
+ public boolean didFinishScalingOperation() {
+ if (mFinishedScalingOperation) {
+ mFinishedScalingOperation = false;
+ return true;
+ }
+ return false;
+ }
+
+}