Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4122499 authored by Ruben Brunk's avatar Ruben Brunk
Browse files

Reworked crop/transform UI.

Bug: 7385644
Bug: 7378335
Bug: 7366075

Change-Id: Iee7e0a0741b69269b08726bad45e29d86834054e
parent 67fefdb9
Loading
Loading
Loading
Loading
+5 −74
Original line number Diff line number Diff line
@@ -139,14 +139,7 @@ public class PanelController implements OnClickListener {
        private boolean mShowParameterValue = false;
        private View mAspectButton = null;
        private View mCurvesButton = null;
        private int mCurrentAspectButton = 0;
        private static final int NUMBER_OF_ASPECT_BUTTONS = 6;
        private static final int ASPECT_NONE = 0;
        private static final int ASPECT_1TO1 = 1;
        private static final int ASPECT_5TO7 = 2;
        private static final int ASPECT_4TO6 = 3;
        private static final int ASPECT_16TO9 = 4;
        private static final int ASPECT_ORIG = 5;
        boolean firstTimeCropDisplayed = true;

        public UtilityPanel(Context context, View view, View textView,
                View aspectButton, View curvesButton) {
@@ -198,69 +191,6 @@ public class PanelController implements OnClickListener {
            imageCrop.invalidate();
        }

        public void nextAspectButton() {
            if (mAspectButton instanceof ImageButtonTitle
                    && mCurrentImage instanceof ImageCrop) {
                switch (mCurrentAspectButton) {
                    case ASPECT_NONE:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspect1to1_effect));
                        ((ImageCrop) mCurrentImage).apply(1, 1);
                        break;
                    case ASPECT_1TO1:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspect5to7_effect));
                        ((ImageCrop) mCurrentImage).apply(7, 5);
                        break;
                    case ASPECT_5TO7:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspect4to6_effect));
                        ((ImageCrop) mCurrentImage).apply(6, 4);
                        break;
                    case ASPECT_4TO6:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspect9to16_effect));
                        ((ImageCrop) mCurrentImage).apply(16, 9);
                        break;
                    case ASPECT_16TO9:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspectOriginal_effect));
                        ((ImageCrop) mCurrentImage).applyOriginal();
                        break;
                    case ASPECT_ORIG:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspectNone_effect));
                        ((ImageCrop) mCurrentImage).applyClear();
                        break;
                    default:
                        ((ImageButtonTitle) mAspectButton).setText(mContext
                                .getString(R.string.aspect)
                                + " "
                                + mContext.getString(R.string.aspectNone_effect));
                        ((ImageCrop) mCurrentImage).applyClear();
                        mCurrentAspectButton = ASPECT_NONE;
                        break;
                }
                mCurrentAspectButton = (mCurrentAspectButton + 1) % NUMBER_OF_ASPECT_BUTTONS;
            }
        }

        void setCurrentAspectButton(int n) {
            mCurrentAspectButton = n;
        }

        public void showAspectButtons() {
            if (mAspectButton != null)
                mAspectButton.setVisibility(View.VISIBLE);
@@ -653,8 +583,10 @@ public class PanelController implements OnClickListener {
                String ename = mCurrentImage.getContext().getString(R.string.crop);
                mUtilityPanel.setEffectName(ename);
                mUtilityPanel.setShowParameter(false);
                mUtilityPanel.setCurrentAspectButton(-1);
                mUtilityPanel.nextAspectButton();
                if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed){
                    ((ImageCrop) mCurrentImage).applyOriginal();
                    mUtilityPanel.firstTimeCropDisplayed = false;
                }
                mUtilityPanel.showAspectButtons();
                break;
            }
@@ -760,7 +692,6 @@ public class PanelController implements OnClickListener {
                break;
            }
            case R.id.aspect: {
                mUtilityPanel.nextAspectButton();
                mUtilityPanel.showAspectButtons();
                break;
            }
+7 −7
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;

import com.android.gallery3d.filtershow.imageshow.GeometryMath;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;

public class ImageFilterGeometry extends ImageFilter {
@@ -78,19 +79,18 @@ public class ImageFilterGeometry extends ImageFilter {
        } else {
            temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig);
        }
        float[] displayCenter = {
                temp.getWidth() / 2f, temp.getHeight() / 2f
        };

        Matrix m1 = mGeometry.buildTotalXform(bitmap.getWidth(), bitmap.getHeight(), displayCenter);

        RectF rp = mGeometry.getPhotoBounds();
        RectF rc = mGeometry.getPreviewCropBounds();
        Matrix drawMatrix = mGeometry.buildTotalXform(rp.width(), rp.height(), rc.width(),
                rc.height(), rc.left, rc.top,
                mGeometry.getRotation(), mGeometry.getStraightenRotation(),
                bitmap.getWidth() / rp.width(), null);
        Canvas canvas = new Canvas(temp);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawBitmap(bitmap, drawMatrix, paint);
        canvas.drawBitmap(bitmap, m1, paint);
        return temp;
    }

+35 −9
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package com.android.gallery3d.filtershow.imageshow;

import android.graphics.RectF;

public class GeometryMath {
    protected static float clamp(float i, float low, float high) {

    // Math operations for 2d vectors
    public static float clamp(float i, float low, float high) {
        return Math.max(Math.min(i, high), low);
    }

@@ -35,36 +39,58 @@ public class GeometryMath {
        float[] ret = {
                (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
        };
        return ret;
        float [] vec = {ret[0] - point[0], ret[1] - point[1] };
        return vec;
    }

    // A . B
    protected static float dotProduct(float[] a, float[] b){
    public static float dotProduct(float[] a, float[] b){
        return a[0] * b[0] + a[1] * b[1];
    }

    protected static float[] normalize(float[] a){
    public static float[] normalize(float[] a){
        float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
        float[] b = { a[0] / length, a[1] / length };
        return b;
    }

    // A onto B
    protected static float scalarProjection(float[] a, float[] b){
    public static float scalarProjection(float[] a, float[] b){
        float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]);
        return dotProduct(a, b) / length;
    }

    protected static float[] getVectorFromPoints(float [] point1, float [] point2){
    public static float[] getVectorFromPoints(float [] point1, float [] point2){
        float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
        return p;
    }

    protected static float[] getUnitVectorFromPoints(float [] point1, float [] point2){
    public static float[] getUnitVectorFromPoints(float [] point1, float [] point2){
        float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
        float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]);
        p[0] = p[0] / length;
        p[1] = p[1] / length;
        return p;
    }

    public static RectF scaleRect(RectF r, float scale){
        return new RectF(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale);
    }

    // A - B
    public static float[] vectorSubtract(float [] a, float [] b){
        int len = a.length;
        if (len != b.length)
            return null;
        float [] ret = new float[len];
        for (int i = 0; i < len; i++){
            ret[i] = a[i] - b[i];
        }
        return ret;
    }

    public static float vectorLength(float [] a){
        return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
    }

}
+149 −20
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.graphics.RectF;

import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;

import java.util.Arrays;

public class GeometryMetadata {
    // Applied in order: rotate, crop, scale.
    // Do not scale saved image (presumably?).
@@ -178,22 +180,34 @@ public class GeometryMetadata {
                + ",photoRect=" + mPhotoBounds.toShortString() + "]";
    }

    protected Matrix getHorizontalMatrix(float width) {
    // TODO: refactor away
    protected static Matrix getHorizontalMatrix(float width) {
        Matrix flipHorizontalMatrix = new Matrix();
        flipHorizontalMatrix.setScale(-1, 1);
        flipHorizontalMatrix.postTranslate(width, 0);
        return flipHorizontalMatrix;
    }

    protected Matrix getVerticalMatrix(float height) {
    protected static void concatHorizontalMatrix(Matrix m, float width) {
        m.postScale(-1, 1);
        m.postTranslate(width, 0);
    }

    // TODO: refactor away
    protected static Matrix getVerticalMatrix(float height) {
        Matrix flipVerticalMatrix = new Matrix();
        flipVerticalMatrix.setScale(1, -1);
        flipVerticalMatrix.postTranslate(0, height);
        return flipVerticalMatrix;
    }

    public Matrix getFlipMatrix(float width, float height) {
        FLIP type = getFlipType();
    protected static void concatVerticalMatrix(Matrix m, float height) {
        m.postScale(1, -1);
        m.postTranslate(0, height);
    }

    // TODO: refactor away
    public static Matrix getFlipMatrix(float width, float height, FLIP type) {
        if (type == FLIP.HORIZONTAL) {
            return getHorizontalMatrix(width);
        } else if (type == FLIP.VERTICAL) {
@@ -209,10 +223,28 @@ public class GeometryMetadata {
        }
    }

    public static void concatMirrorMatrix(Matrix m, float width, float height, FLIP type) {
        if (type == FLIP.HORIZONTAL) {
            concatHorizontalMatrix(m, width);
        } else if (type == FLIP.VERTICAL) {
            concatVerticalMatrix(m, height);
        } else if (type == FLIP.BOTH) {
            concatVerticalMatrix(m, height);
            concatHorizontalMatrix(m, width);
        }
    }

    // TODO: refactor away
    public Matrix getFlipMatrix(float width, float height) {
        FLIP type = getFlipType();
        return getFlipMatrix(width, height, type);
    }

    public boolean hasSwitchedWidthHeight() {
        return (((int) (mRotation / 90)) % 2) != 0;
    }

    // TODO: refactor away
    public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
            float rotation) {
        float dx0 = width / 2;
@@ -225,6 +257,7 @@ public class GeometryMetadata {
        return m;
    }

    // TODO: refactor away
    public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
            boolean onlyRotate) {
        float rot = mRotation;
@@ -234,28 +267,124 @@ public class GeometryMetadata {
        return buildGeometryMatrix(width, height, scaling, dx, dy, rot);
    }

    // TODO: refactor away
    public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) {
        float w = mPhotoBounds.width();
        float h = mPhotoBounds.height();
        return buildGeometryMatrix(w, h, scaling, dx, dy, false);
    }

    public Matrix buildTotalXform(float pwidth, float pheight, float cwidth, float cheight,
            float cleft, float ctop, float rotation, float straighten, float scale, RectF dst) {
        float s_pwidth = pwidth * scale;
        float s_pheight = pheight * scale;
        Matrix m = getFlipMatrix(s_pwidth, s_pheight);
        m.postRotate(rotation + straighten, s_pwidth / 2, s_pheight / 2);
        Matrix m1 = getFlipMatrix(s_pwidth, s_pheight);
        m1.postRotate(rotation, s_pwidth / 2, s_pheight / 2);
        // find new top left for crop.
        RectF crop = new RectF(cleft * scale, ctop * scale, (cleft + cwidth) * scale,
                (ctop + cheight) * scale);
        if (!m1.mapRect(crop))
            return null;
        if (dst != null)
            dst.set(crop);
        m.postTranslate(-crop.left, -crop.top);
    public static Matrix buildPhotoMatrix(RectF photo, RectF crop, float rotation,
            float straighten, FLIP type) {
        Matrix m = new Matrix();
        m.setRotate(straighten, photo.centerX(), photo.centerY());
        concatMirrorMatrix(m, photo.right, photo.bottom, type);
        m.postRotate(rotation, crop.centerX(), crop.centerY());

        return m;
    }

    public static Matrix buildCropMatrix(RectF crop, float rotation) {
        Matrix m = new Matrix();
        m.setRotate(rotation, crop.centerX(), crop.centerY());
        return m;
    }

    public static void concatRecenterMatrix(Matrix m, float[] currentCenter, float[] newCenter) {
        m.postTranslate(newCenter[0] - currentCenter[0], newCenter[1] - currentCenter[1]);
    }

    /**
     * Builds a matrix to transform a bitmap of width bmWidth and height
     * bmHeight so that the region of the bitmap being cropped to is
     * oriented and centered at displayCenter.
     *
     * @param bmWidth
     * @param bmHeight
     * @param displayCenter
     * @return
     */
    public Matrix buildTotalXform(float bmWidth, float bmHeight, float[] displayCenter) {
        RectF rp = getPhotoBounds();
        RectF rc = getPreviewCropBounds();

        float scale = bmWidth / rp.width();
        RectF scaledCrop = GeometryMath.scaleRect(rc, scale);
        RectF scaledPhoto = GeometryMath.scaleRect(rp, scale);

        Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop,
                getRotation(), getStraightenRotation(),
                getFlipType(), displayCenter);
        float[] cropCenter = {
                scaledCrop.centerX(), scaledCrop.centerY()
        };
        m1.mapPoints(cropCenter);

        GeometryMetadata.concatRecenterMatrix(m1, cropCenter, displayCenter);
        m1.preRotate(getStraightenRotation(), scaledPhoto.centerX(),
                scaledPhoto.centerY());
        return m1;
    }

    /**
     * Builds a matrix that rotates photo rect about it's center by the
     * straighten angle, mirrors it about the crop center, and rotates it about
     * the crop center by the rotation angle, and re-centers the photo rect.
     *
     * @param photo
     * @param crop
     * @param rotation
     * @param straighten
     * @param type
     * @param newCenter
     * @return
     */
    public static Matrix buildCenteredPhotoMatrix(RectF photo, RectF crop, float rotation,
            float straighten, FLIP type, float[] newCenter) {
        Matrix m = buildPhotoMatrix(photo, crop, rotation, straighten, type);
        float[] center = {
                photo.centerX(), photo.centerY()
        };
        m.mapPoints(center);
        concatRecenterMatrix(m, center, newCenter);
        return m;
    }

    /**
     * Builds a matrix that rotates a crop rect about it's center by rotation
     * angle, then re-centers the crop rect.
     *
     * @param crop
     * @param rotation
     * @param newCenter
     * @return
     */
    public static Matrix buildCenteredCropMatrix(RectF crop, float rotation, float[] newCenter) {
        Matrix m = buildCropMatrix(crop, rotation);
        float[] center = {
                crop.centerX(), crop.centerY()
        };
        m.mapPoints(center);
        concatRecenterMatrix(m, center, newCenter);
        return m;
    }

    /**
     * Builds a matrix that transforms the crop rect to its view coordinates
     * inside the photo rect.
     *
     * @param photo
     * @param crop
     * @param rotation
     * @param straighten
     * @param type
     * @param newCenter
     * @return
     */
    public static Matrix buildWanderingCropMatrix(RectF photo, RectF crop, float rotation,
            float straighten, FLIP type, float[] newCenter) {
        Matrix m = buildCenteredPhotoMatrix(photo, crop, rotation, straighten, type, newCenter);
        m.preRotate(-straighten, photo.centerX(), photo.centerY());
        return m;
    }
}
+25 −9
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ public class ImageCrop extends ImageGeometry {
    private float mAspectHeight = 1;
    private boolean mFixAspectRatio = false;

    private float mLastRot = 0;
    private final Paint borderPaint;

    private int movingEdges;
@@ -90,6 +91,12 @@ public class ImageCrop extends ImageGeometry {
        return getContext().getString(R.string.crop);
    }

    private void swapAspect(){
        float temp = mAspectWidth;
        mAspectWidth = mAspectHeight;
        mAspectHeight = temp;
    }

    private boolean switchCropBounds(int moving_corner, RectF dst) {
        RectF crop = getCropBoundsDisplayed();
        float dx1 = 0;
@@ -144,9 +151,7 @@ public class ImageCrop extends ImageGeometry {
                     Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
                 return false;
             }
             float temp = mAspectWidth;
             mAspectWidth = mAspectHeight;
             mAspectHeight = temp;
             swapAspect();
             dst.set(newCrop);
             return true;
        }
@@ -565,6 +570,11 @@ public class ImageCrop extends ImageGeometry {

    @Override
    protected void gainedVisibility() {
        float rot = getLocalRotation();
        // if has changed orientation via rotate
        if( ((int) ((rot - mLastRot) / 90)) % 2 != 0 ){
            swapAspect();
        }
        cropSetup();
        mFirstDraw = true;
    }
@@ -577,19 +587,20 @@ public class ImageCrop extends ImageGeometry {

    @Override
    protected void lostVisibility() {
        mLastRot = getLocalRotation();
    }

    private void drawRuleOfThird(Canvas canvas, RectF bounds) {
    private void drawRuleOfThird(Canvas canvas, RectF bounds, Paint p) {
        float stepX = bounds.width() / 3.0f;
        float stepY = bounds.height() / 3.0f;
        float x = bounds.left + stepX;
        float y = bounds.top + stepY;
        for (int i = 0; i < 2; i++) {
            canvas.drawLine(x, bounds.top, x, bounds.bottom, gPaint);
            canvas.drawLine(x, bounds.top, x, bounds.bottom, p);
            x += stepX;
        }
        for (int j = 0; j < 2; j++) {
            canvas.drawLine(bounds.left, y, bounds.right, y, gPaint);
            canvas.drawLine(bounds.left, y, bounds.right, y, p);
            y += stepY;
        }
    }
@@ -607,17 +618,22 @@ public class ImageCrop extends ImageGeometry {
            mFirstDraw = false;
        }
        float rotation = getLocalRotation();
        drawTransformedBitmap(canvas, image, gPaint, true);

        RectF crop = drawTransformed(canvas, image, gPaint);
        gPaint.setColor(mBorderColor);
        gPaint.setStrokeWidth(3);
        gPaint.setStyle(Paint.Style.STROKE);
        drawRuleOfThird(canvas, crop, gPaint);

        gPaint.setColor(mBorderColor);
        gPaint.setStrokeWidth(3);
        gPaint.setStyle(Paint.Style.STROKE);
        drawStraighten(canvas, gPaint);
        RectF scaledCrop = unrotatedCropBounds();
        drawRuleOfThird(canvas, scaledCrop);

        int decoded_moving = decoder(movingEdges, rotation);
        canvas.save();
        canvas.rotate(rotation, mCenterX, mCenterY);
        RectF scaledCrop = unrotatedCropBounds();
        boolean notMoving = decoded_moving == 0;
        if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
            drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
Loading