FileDocCategorySizeDatePackage
HighlightView.javaAPI DocAndroid 1.5 API15630Wed May 06 22:42:42 BST 2009com.android.camera

HighlightView.java

/*
 * Copyright (C) 2007 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.camera;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.Config;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;


public class HighlightView
{
    private static final String TAG = "CropImage";
    View mContext;
    Path mPath;
    Rect mViewDrawingRect = new Rect();

    int mMotionMode;
    
    public static final int GROW_NONE        = (1 << 0);
    public static final int GROW_LEFT_EDGE   = (1 << 1);
    public static final int GROW_RIGHT_EDGE  = (1 << 2);
    public static final int GROW_TOP_EDGE    = (1 << 3);
    public static final int GROW_BOTTOM_EDGE = (1 << 4);
    public static final int MOVE             = (1 << 5);
    
    public HighlightView(View ctx) 
    {
        super();
        mContext = ctx;
        mPath = new Path();
    }

    private void initHighlightView() {
        android.content.res.Resources resources = mContext.getResources();
        mResizeDrawableWidth  = resources.getDrawable(R.drawable.camera_crop_width);
        mResizeDrawableHeight = resources.getDrawable(R.drawable.camera_crop_height);
        mResizeDrawableDiagonal = resources.getDrawable(R.drawable.indicator_autocrop);
    }
    
    boolean mIsFocused;
    boolean mHidden;
    
    public boolean hasFocus() {
        return mIsFocused;
    }
    
    public void setFocus(boolean f) {
        mIsFocused = f;
    }
    
    public void setHidden(boolean hidden) {
        mHidden = hidden;
    }
    
    protected void draw(Canvas canvas) {
        if (mHidden)
            return;
        
        canvas.save();
        mPath.reset();
        if (!hasFocus()) {
            mOutlinePaint.setColor(0xFF000000);
            canvas.drawRect(mDrawRect, mOutlinePaint);
        } else {
            mContext.getDrawingRect(mViewDrawingRect);
            if (mCircle) {
                float width  = mDrawRect.width()  - (getPaddingLeft() + getPaddingRight());
                float height = mDrawRect.height() - (getPaddingTop()  + getPaddingBottom());
                mPath.addCircle(
                        mDrawRect.left + getPaddingLeft() + (width  / 2), 
                        mDrawRect.top  + getPaddingTop()  + (height / 2),
                        width / 2, 
                        Path.Direction.CW);
                mOutlinePaint.setColor(0xFFEF04D6);
            } else {
                mPath.addRect(new RectF(mDrawRect), Path.Direction.CW);
                mOutlinePaint.setColor(0xFFFF8A00);
            }
            canvas.clipPath(mPath, Region.Op.DIFFERENCE);
            canvas.drawRect(mViewDrawingRect, hasFocus() ? mFocusPaint : mNoFocusPaint);

            canvas.restore();
            canvas.drawPath(mPath, mOutlinePaint);

            if (mMode == ModifyMode.Grow) {
                if (mCircle) {
                    int width  = mResizeDrawableDiagonal.getIntrinsicWidth();
                    int height = mResizeDrawableDiagonal.getIntrinsicHeight();
                    
                    int d  = (int) Math.round(Math.cos(/*45deg*/Math.PI/4D) * (mDrawRect.width() / 2D));
                    int x  = mDrawRect.left + (mDrawRect.width() / 2) + d - width/2;
                    int y  = mDrawRect.top  + (mDrawRect.height() / 2) - d - height/2;
                    mResizeDrawableDiagonal.setBounds(x, y, x + mResizeDrawableDiagonal.getIntrinsicWidth(), y + mResizeDrawableDiagonal.getIntrinsicHeight());
                    mResizeDrawableDiagonal.draw(canvas);
                } else {
                    int left    = mDrawRect.left   + 1;
                    int right   = mDrawRect.right  + 1;
                    int top     = mDrawRect.top    + 4;
                    int bottom  = mDrawRect.bottom + 3;

                    int widthWidth   = mResizeDrawableWidth.getIntrinsicWidth() / 2;
                    int widthHeight  = mResizeDrawableWidth.getIntrinsicHeight()/ 2;
                    int heightHeight = mResizeDrawableHeight.getIntrinsicHeight()/ 2;
                    int heightWidth  = mResizeDrawableHeight.getIntrinsicWidth()/ 2;

                    int xMiddle = mDrawRect.left + ((mDrawRect.right  - mDrawRect.left) / 2);
                    int yMiddle = mDrawRect.top  + ((mDrawRect.bottom - mDrawRect.top ) / 2);

                    mResizeDrawableWidth.setBounds(left-widthWidth, yMiddle-widthHeight, left+widthWidth, yMiddle+widthHeight);
                    mResizeDrawableWidth.draw(canvas);

                    mResizeDrawableWidth.setBounds(right-widthWidth, yMiddle-widthHeight, right+widthWidth, yMiddle+widthHeight);
                    mResizeDrawableWidth.draw(canvas);

                    mResizeDrawableHeight.setBounds(xMiddle-heightWidth, top-heightHeight, xMiddle+heightWidth, top+heightHeight); 
                    mResizeDrawableHeight.draw(canvas);

                    mResizeDrawableHeight.setBounds(xMiddle-heightWidth, bottom-heightHeight, xMiddle+heightWidth, bottom+heightHeight); 
                    mResizeDrawableHeight.draw(canvas);
                }
            }
        }
    
    }
    
    float getPaddingTop() { return 0F; }
    float getPaddingBottom() { return 0F; }
    float getPaddingLeft() { return 0F; }
    float getPaddingRight() { return 0F; }

    public ModifyMode getMode() {
        return mMode;
    }
    
    public void setMode(ModifyMode mode)
    {
        if (mode != mMode) {
            mMode = mode;
            mContext.invalidate();
        }
    }
    
    public int getHit(float x, float y) {
        Rect r = computeLayout();
        final float hysteresis = 20F;
        int retval = GROW_NONE;
        
        if (mCircle) {
            float distX = x - r.centerX();
            float distY = y - r.centerY();
            int distanceFromCenter = (int) Math.sqrt(distX*distX + distY*distY);
            int radius  = (int) (mDrawRect.width() - getPaddingLeft()) / 2;
            int delta = distanceFromCenter - radius;
            if (Math.abs(delta) <= hysteresis) {
                if (Math.abs(distY) > Math.abs(distX)) {
                    if (distY < 0)
                        retval = GROW_TOP_EDGE;
                    else
                        retval = GROW_BOTTOM_EDGE;
                } else {
                    if (distX < 0)
                        retval = GROW_LEFT_EDGE;
                    else
                        retval = GROW_RIGHT_EDGE;
                }
            } else if (distanceFromCenter < radius) {
                retval = MOVE;
            } else {
                retval = GROW_NONE;
            }
//          Log.v(TAG, "radius: " + radius + "; touchRadius: " + distanceFromCenter + "; distX: " + distX + "; distY: " + distY + "; retval: " + retval); 
        } else {
            boolean verticalCheck = (y >= r.top - hysteresis) && (y < r.bottom + hysteresis);
            boolean horizCheck = (x >= r.left - hysteresis) && (x < r.right + hysteresis);

            if ((Math.abs(r.left - x)     < hysteresis)  &&  verticalCheck)
                retval |= GROW_LEFT_EDGE;
            if ((Math.abs(r.right - x)    < hysteresis)  &&  verticalCheck)
                retval |= GROW_RIGHT_EDGE;
            if ((Math.abs(r.top - y)      < hysteresis)  &&  horizCheck)
                retval |= GROW_TOP_EDGE;
            if ((Math.abs(r.bottom - y)   < hysteresis)  &&  horizCheck)
                retval |= GROW_BOTTOM_EDGE;

            if (retval == GROW_NONE && r.contains((int)x, (int)y))
                retval = MOVE;
        }
        return retval;
    }

    void handleMotion(int edge, float dx, float dy) {
        Rect r = computeLayout();
        if (edge == GROW_NONE) {
            return;
        } else if (edge == MOVE) {
            moveBy(dx * (mCropRect.width() / r.width()),
                   dy * (mCropRect.height() / r.height()));
        } else {
            if (((GROW_LEFT_EDGE | GROW_RIGHT_EDGE) & edge) == 0)
                dx = 0;

            if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & edge) == 0)
                dy = 0;

            float xDelta = dx * (mCropRect.width() / r.width());
            float yDelta = dy * (mCropRect.height() / r.height());
            growBy((((edge & GROW_LEFT_EDGE) != 0) ? -1 : 1) * xDelta,
                    (((edge & GROW_TOP_EDGE) != 0) ? -1 : 1) * yDelta);
                   
        }
//        Log.v(TAG, "ratio is now " + this.mCropRect.width() / this.mCropRect.height());
    }
    
    void moveBy(float dx, float dy) {
        Rect invalRect = new Rect(mDrawRect);

        mCropRect.offset(dx, dy);
        mCropRect.offset(
                Math.max(0, mImageRect.left - mCropRect.left), 
                Math.max(0, mImageRect.top  - mCropRect.top));

        mCropRect.offset(
                Math.min(0, mImageRect.right  - mCropRect.right), 
                Math.min(0, mImageRect.bottom - mCropRect.bottom));                

        mDrawRect = computeLayout();
        invalRect.union(mDrawRect);
        invalRect.inset(-10, -10);
        mContext.invalidate(invalRect);
    }
    
    private void shift(RectF r, float dx, float dy) {
        r.left   += dx;
        r.right  += dx;
        r.top    += dy;
        r.bottom += dy;
    }

    void growBy(float dx, float dy) {
//      Log.v(TAG, "growBy: " + dx + " " + dy + "; rect w/h is " + mCropRect.width() + " / " + mCropRect.height());
        if (mMaintainAspectRatio) {
            if (dx != 0) {
                dy = dx / mInitialAspectRatio;
            } else if (dy != 0) {
                dx = dy * mInitialAspectRatio;
            }
        }

        RectF r = new RectF(mCropRect);
        if (dx > 0F && r.width() + 2 * dx > mImageRect.width()) {
            float adjustment = (mImageRect.width() - r.width()) / 2F;
            dx = adjustment;
            if (mMaintainAspectRatio)
                dy = dx / mInitialAspectRatio;
        }
        if (dy > 0F && r.height() + 2 * dy > mImageRect.height()) {
            float adjustment = (mImageRect.height() - r.height()) / 2F;
            dy = adjustment;
            if (mMaintainAspectRatio)
                dx = dy * mInitialAspectRatio;
        }

        r.inset(-dx, -dy);
        
        float widthCap = 25F;
        if (r.width() < 25) {
            r.inset(-(25F-r.width())/2F, 0F);
        }
        float heightCap = mMaintainAspectRatio ? (widthCap / mInitialAspectRatio) : widthCap;
        if (r.height() < heightCap) {
            r.inset(0F, -(heightCap-r.height())/2F);
        }
        
        if (r.left < mImageRect.left) {
            shift(r, mImageRect.left - r.left, 0F);
        } else if (r.right > mImageRect.right) {
            shift(r, -(r.right - mImageRect.right), 0);
        }
        if (r.top < mImageRect.top) {
            shift(r, 0F, mImageRect.top - r.top);
        } else if (r.bottom > mImageRect.bottom) {
            shift(r, 0F, -(r.bottom - mImageRect.bottom));
        }
/*        
        RectF rCandidate = new RectF(r);
        r.intersect(mImageRect);
        if (mMaintainAspectRatio) {
            if (r.left != rCandidate.left) {
                Log.v(TAG, "bail 1");
                return;
            }
            if (r.right != rCandidate.right) {
                Log.v(TAG, "bail 2");
                return;
            }
            if (r.top != rCandidate.top) {
                Log.v(TAG, "bail 3");
                return;
            }
            if (r.bottom != rCandidate.bottom) {
                Log.v(TAG, "bail 4");
                return;
            }
        }
*/        
        mCropRect.set(r);
        mDrawRect = computeLayout();
        mContext.invalidate();
    }
    
    public Rect getCropRect() {
        return new Rect((int)mCropRect.left, (int)mCropRect.top, (int)mCropRect.right, (int)mCropRect.bottom);
    }
    
    private Rect computeLayout() {
        RectF r = new RectF(mCropRect.left, mCropRect.top, mCropRect.right, mCropRect.bottom);
        mMatrix.mapRect(r);
        return new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), Math.round(r.bottom));
    }
    
    public void invalidate() {
        mDrawRect = computeLayout();
    }
    
    public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean circle, boolean maintainAspectRatio) {
        if (Config.LOGV) Log.v(TAG, "setup... " + imageRect + "; " + cropRect + "; maintain " + maintainAspectRatio + "; circle " + circle);
        if (circle)
            maintainAspectRatio = true;
        mMatrix = new Matrix(m);

        mCropRect = cropRect;
        mImageRect = new RectF(imageRect);
        mMaintainAspectRatio = maintainAspectRatio;
        mCircle = circle;

        mInitialAspectRatio = mCropRect.width() / mCropRect.height();
        mDrawRect = computeLayout();
        
        mFocusPaint.setARGB(125, 50, 50, 50);
        mNoFocusPaint.setARGB(125, 50, 50, 50);
        mOutlinePaint.setStrokeWidth(3F);
        mOutlinePaint.setStyle(Paint.Style.STROKE);
        mOutlinePaint.setAntiAlias(true);

        mMode = ModifyMode.None;
        initHighlightView();
    }
    
    public void modify(int keyCode, long repeatCount)
    {
        float factor = Math.max(.01F, Math.min(.1F, repeatCount * .01F));  
        float widthUnits = factor * (float)mContext.getWidth();
        float heightUnits = widthUnits;

        switch (keyCode)
        {       
        case KeyEvent.KEYCODE_DPAD_LEFT:
            if (mMode == ModifyMode.Move)
                moveBy(-widthUnits, 0);
            else if (mMode == ModifyMode.Grow)
                growBy(-widthUnits, 0);
            break;
        
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            if (mMode == ModifyMode.Move)
                moveBy(widthUnits, 0);
            else if (mMode == ModifyMode.Grow)
                growBy(widthUnits, 0);
            break;
    
        case KeyEvent.KEYCODE_DPAD_UP:
            if (mMode == ModifyMode.Move)
                moveBy(0, -heightUnits);
            else if (mMode == ModifyMode.Grow)
                growBy(0, -heightUnits);
            break;

        case KeyEvent.KEYCODE_DPAD_DOWN:
            if (mMode == ModifyMode.Move)
                moveBy(0, heightUnits);
            else if (mMode == ModifyMode.Grow)
                growBy(0, heightUnits);
            break;
        }
    }
    
    enum ModifyMode { None, Move,Grow };
    
    ModifyMode mMode = ModifyMode.None;
    
    Rect  mDrawRect;
    RectF mImageRect;
    RectF mCropRect;
    Matrix mMatrix;

    boolean mMaintainAspectRatio = false;
    float mInitialAspectRatio;
    boolean mCircle = false;
    
    Drawable mResizeDrawableWidth, mResizeDrawableHeight, mResizeDrawableDiagonal;

    Paint mFocusPaint = new Paint();
    Paint mNoFocusPaint = new Paint();
    Paint mOutlinePaint = new Paint();
}