Home | History | Annotate | Download | only in imageshow
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.filtershow.imageshow;
     18 
     19 import android.animation.ValueAnimator;
     20 import android.content.Context;
     21 import android.graphics.Bitmap;
     22 import android.graphics.Canvas;
     23 import android.graphics.Color;
     24 import android.graphics.Matrix;
     25 import android.graphics.Paint;
     26 import android.graphics.Paint.Style;
     27 import android.graphics.Path;
     28 import android.graphics.RectF;
     29 import android.util.AttributeSet;
     30 import android.view.MotionEvent;
     31 
     32 import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
     33 import com.android.gallery3d.filtershow.editors.EditorStraighten;
     34 import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
     35 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
     36 import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
     37 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Collection;
     41 
     42 
     43 public class ImageStraighten extends ImageShow {
     44     private static final String TAG = ImageStraighten.class.getSimpleName();
     45     private float mBaseAngle = 0;
     46     private float mAngle = 0;
     47     private float mInitialAngle = 0;
     48     private static final int NBLINES = 16;
     49     private boolean mFirstDrawSinceUp = false;
     50     private EditorStraighten mEditorStraighten;
     51     private FilterStraightenRepresentation mLocalRep = new FilterStraightenRepresentation();
     52     private RectF mPriorCropAtUp = new RectF();
     53     private RectF mDrawRect = new RectF();
     54     private Path mDrawPath = new Path();
     55     private GeometryHolder mDrawHolder = new GeometryHolder();
     56     private enum MODES {
     57         NONE, MOVE
     58     }
     59     private MODES mState = MODES.NONE;
     60     private ValueAnimator mAnimator = null;
     61     private int mDefaultGridAlpha = 60;
     62     private float mGridAlpha = 1f;
     63     private int mOnStartAnimDelay = 1000;
     64     private int mAnimDelay = 500;
     65     private static final float MAX_STRAIGHTEN_ANGLE
     66         = FilterStraightenRepresentation.MAX_STRAIGHTEN_ANGLE;
     67     private static final float MIN_STRAIGHTEN_ANGLE
     68         = FilterStraightenRepresentation.MIN_STRAIGHTEN_ANGLE;
     69     private float mCurrentX;
     70     private float mCurrentY;
     71     private float mTouchCenterX;
     72     private float mTouchCenterY;
     73     private RectF mCrop = new RectF();
     74     private final Paint mPaint = new Paint();
     75 
     76     public ImageStraighten(Context context) {
     77         super(context);
     78     }
     79 
     80     public ImageStraighten(Context context, AttributeSet attrs) {
     81         super(context, attrs);
     82     }
     83 
     84     @Override
     85     public void attach() {
     86         super.attach();
     87         mGridAlpha = 1f;
     88         hidesGrid(mOnStartAnimDelay);
     89     }
     90 
     91     private void hidesGrid(int delay) {
     92         mAnimator = ValueAnimator.ofFloat(1, 0);
     93         mAnimator.setStartDelay(delay);
     94         mAnimator.setDuration(mAnimDelay);
     95         mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
     96             @Override
     97             public void onAnimationUpdate(ValueAnimator animation) {
     98                 mGridAlpha = ((Float) animation.getAnimatedValue());
     99                 invalidate();
    100             }
    101         });
    102         mAnimator.start();
    103     }
    104 
    105     public void setFilterStraightenRepresentation(FilterStraightenRepresentation rep) {
    106         mLocalRep = (rep == null) ? new FilterStraightenRepresentation() : rep;
    107         mInitialAngle = mBaseAngle = mAngle = mLocalRep.getStraighten();
    108     }
    109 
    110     public Collection<FilterRepresentation> getFinalRepresentation() {
    111         ArrayList<FilterRepresentation> reps = new ArrayList<FilterRepresentation>(2);
    112         reps.add(mLocalRep);
    113         if (mInitialAngle != mLocalRep.getStraighten()) {
    114             reps.add(new FilterCropRepresentation(mCrop));
    115         }
    116         return reps;
    117     }
    118 
    119     @Override
    120     public boolean onTouchEvent(MotionEvent event) {
    121         float x = event.getX();
    122         float y = event.getY();
    123 
    124         switch (event.getActionMasked()) {
    125             case (MotionEvent.ACTION_DOWN):
    126                 if (mState == MODES.NONE) {
    127                     mTouchCenterX = x;
    128                     mTouchCenterY = y;
    129                     mCurrentX = x;
    130                     mCurrentY = y;
    131                     mState = MODES.MOVE;
    132                     mBaseAngle = mAngle;
    133                 }
    134                 break;
    135             case (MotionEvent.ACTION_UP):
    136                 if (mState == MODES.MOVE) {
    137                     mState = MODES.NONE;
    138                     mCurrentX = x;
    139                     mCurrentY = y;
    140                     computeValue();
    141                     mFirstDrawSinceUp = true;
    142                     hidesGrid(0);
    143                 }
    144                 break;
    145             case (MotionEvent.ACTION_MOVE):
    146                 if (mState == MODES.MOVE) {
    147                     mCurrentX = x;
    148                     mCurrentY = y;
    149                     computeValue();
    150                 }
    151                 break;
    152             default:
    153                 break;
    154         }
    155         invalidate();
    156         return true;
    157     }
    158 
    159     private static float angleFor(float dx, float dy) {
    160         return (float) (Math.atan2(dx, dy) * 180 / Math.PI);
    161     }
    162 
    163     private float getCurrentTouchAngle() {
    164         float centerX = getWidth() / 2f;
    165         float centerY = getHeight() / 2f;
    166         if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
    167             return 0;
    168         }
    169         float dX1 = mTouchCenterX - centerX;
    170         float dY1 = mTouchCenterY - centerY;
    171         float dX2 = mCurrentX - centerX;
    172         float dY2 = mCurrentY - centerY;
    173         float angleA = angleFor(dX1, dY1);
    174         float angleB = angleFor(dX2, dY2);
    175         return (angleB - angleA) % 360;
    176     }
    177 
    178     private void computeValue() {
    179         float angle = getCurrentTouchAngle();
    180         mAngle = (mBaseAngle - angle) % 360;
    181         mAngle = Math.max(MIN_STRAIGHTEN_ANGLE, mAngle);
    182         mAngle = Math.min(MAX_STRAIGHTEN_ANGLE, mAngle);
    183     }
    184 
    185     public static void getUntranslatedStraightenCropBounds(RectF outRect, float straightenAngle) {
    186         float deg = straightenAngle;
    187         if (deg < 0) {
    188             deg = -deg;
    189         }
    190         double a = Math.toRadians(deg);
    191         double sina = Math.sin(a);
    192         double cosa = Math.cos(a);
    193         double rw = outRect.width();
    194         double rh = outRect.height();
    195         double h1 = rh * rh / (rw * sina + rh * cosa);
    196         double h2 = rh * rw / (rw * cosa + rh * sina);
    197         double hh = Math.min(h1, h2);
    198         double ww = hh * rw / rh;
    199         float left = (float) ((rw - ww) * 0.5f);
    200         float top = (float) ((rh - hh) * 0.5f);
    201         float right = (float) (left + ww);
    202         float bottom = (float) (top + hh);
    203         outRect.set(left, top, right, bottom);
    204     }
    205 
    206     private void updateCurrentCrop(Matrix m, GeometryHolder h, RectF tmp, int imageWidth,
    207             int imageHeight, int viewWidth, int viewHeight) {
    208         tmp.set(0, 0, imageHeight, imageWidth);
    209         m.mapRect(tmp);
    210         float top = tmp.top;
    211         float bottom = tmp.bottom;
    212         float left = tmp.left;
    213         float right = tmp.right;
    214         m.mapRect(tmp);
    215         int iw,ih;
    216         if (GeometryMathUtils.needsDimensionSwap(h.rotation)) {
    217             tmp.set(0, 0, imageHeight, imageWidth);
    218             iw = imageHeight;
    219             ih = imageWidth;
    220         } else {
    221             tmp.set(0, 0, imageWidth, imageHeight);
    222             iw = imageWidth;
    223             ih = imageHeight;
    224         }
    225         float scale = GeometryMathUtils.scale(iw, ih, viewWidth, viewHeight);
    226         scale *= GeometryMathUtils.SHOW_SCALE;
    227         GeometryMathUtils.scaleRect(tmp, scale);
    228         getUntranslatedStraightenCropBounds(tmp, mAngle);
    229         tmp.offset(viewWidth / 2f - tmp.centerX(), viewHeight / 2f - tmp.centerY());
    230         h.straighten = 0;
    231         Matrix m1 = GeometryMathUtils.getFullGeometryToScreenMatrix(h, imageWidth,
    232                 imageHeight, viewWidth, viewHeight);
    233         m.reset();
    234         m1.invert(m);
    235         mCrop.set(tmp);
    236         m.mapRect(mCrop);
    237         FilterCropRepresentation.findNormalizedCrop(mCrop, imageWidth, imageHeight);
    238     }
    239 
    240 
    241     @Override
    242     public void onDraw(Canvas canvas) {
    243         MasterImage master = MasterImage.getImage();
    244         Bitmap image = master.getFiltersOnlyImage();
    245         if (image == null) {
    246             MasterImage.getImage().invalidateFiltersOnly();
    247             return;
    248         }
    249         GeometryMathUtils.initializeHolder(mDrawHolder, mLocalRep);
    250         mDrawHolder.straighten = mAngle;
    251         int imageWidth = image.getWidth();
    252         int imageHeight = image.getHeight();
    253         int viewWidth = canvas.getWidth();
    254         int viewHeight = canvas.getHeight();
    255 
    256         // Get matrix for drawing bitmap
    257         Matrix m = GeometryMathUtils.getFullGeometryToScreenMatrix(mDrawHolder, imageWidth,
    258                 imageHeight, viewWidth, viewHeight);
    259         mPaint.reset();
    260         mPaint.setAntiAlias(true);
    261         mPaint.setFilterBitmap(true);
    262         canvas.drawBitmap(image, m, mPaint);
    263 
    264         mPaint.setFilterBitmap(false);
    265         mPaint.setColor(Color.WHITE);
    266         mPaint.setStrokeWidth(2);
    267         mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    268         updateCurrentCrop(m, mDrawHolder, mDrawRect, imageWidth,
    269                 imageHeight, viewWidth, viewHeight);
    270         if (mFirstDrawSinceUp) {
    271             mPriorCropAtUp.set(mCrop);
    272             mLocalRep.setStraighten(mAngle);
    273             mFirstDrawSinceUp = false;
    274         }
    275         CropDrawingUtils.drawShade(canvas, mDrawRect);
    276         // Draw the grid
    277         if (mState == MODES.MOVE || mGridAlpha > 0) {
    278             canvas.save();
    279             canvas.clipRect(mDrawRect);
    280 
    281             float step = Math.max(viewWidth, viewHeight) / NBLINES;
    282             float p = 0;
    283             for (int i = 1; i < NBLINES; i++) {
    284                 p = i * step;
    285                 int alpha = (int) (mDefaultGridAlpha * mGridAlpha);
    286                 if (alpha == 0 && mState == MODES.MOVE) {
    287                     alpha = mDefaultGridAlpha;
    288                 }
    289                 mPaint.setAlpha(alpha);
    290                 canvas.drawLine(p, 0, p, viewHeight, mPaint);
    291                 canvas.drawLine(0, p, viewWidth, p, mPaint);
    292             }
    293             canvas.restore();
    294         }
    295         mPaint.reset();
    296         mPaint.setColor(Color.WHITE);
    297         mPaint.setStyle(Style.STROKE);
    298         mPaint.setStrokeWidth(3);
    299         mDrawPath.reset();
    300 
    301 
    302         mDrawPath.addRect(mDrawRect, Path.Direction.CW);
    303         canvas.drawPath(mDrawPath, mPaint);
    304     }
    305 
    306     public void setEditor(EditorStraighten editorStraighten) {
    307         mEditorStraighten = editorStraighten;
    308     }
    309 
    310 }
    311