Home | History | Annotate | Download | only in photoeditor
      1 /*
      2  * Copyright (C) 2010 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.photoeditor;
     18 
     19 import android.content.Context;
     20 import android.graphics.Canvas;
     21 import android.graphics.Matrix;
     22 import android.graphics.Paint;
     23 import android.graphics.Path;
     24 import android.graphics.PointF;
     25 import android.graphics.RectF;
     26 import android.util.AttributeSet;
     27 import android.view.View;
     28 import android.view.animation.Animation;
     29 
     30 import com.android.photoeditor.animation.AnimationPair;
     31 
     32 /**
     33  * Displays photo in the view. All its methods should be called from UI thread.
     34  */
     35 public class PhotoView extends View {
     36 
     37     private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
     38     private final Matrix displayMatrix = new Matrix();
     39     private Photo photo;
     40     private RectF clipBounds;
     41     private AnimationPair transitions;
     42 
     43     public PhotoView(Context context, AttributeSet attrs) {
     44         super(context, attrs);
     45     }
     46 
     47     @Override
     48     protected void onDraw(Canvas canvas) {
     49         super.onDraw(canvas);
     50 
     51         if (photo != null) {
     52             canvas.save();
     53             if (clipBounds != null) {
     54                 canvas.clipRect(clipBounds);
     55             }
     56             canvas.concat(displayMatrix);
     57             canvas.drawBitmap(photo.bitmap(), 0, 0, paint);
     58             canvas.restore();
     59         }
     60     }
     61 
     62     /**
     63      * Maps x and y to a percentage position relative to displayed photo.
     64      */
     65     public PointF mapPhotoPoint(float x, float y) {
     66         if ((photo == null) || (photo.width() == 0) || (photo.height() == 0)) {
     67             return new PointF();
     68         }
     69         float[] point = new float[] {x, y};
     70         Matrix matrix = new Matrix();
     71         displayMatrix.invert(matrix);
     72         matrix.mapPoints(point);
     73         return new PointF(point[0] / photo.width(), point[1] / photo.height());
     74     }
     75 
     76     public void mapPhotoPath(Path src, Path dst) {
     77         // TODO: Use percentages representing paths for saving photo larger than previewed photo.
     78         Matrix matrix = new Matrix();
     79         displayMatrix.invert(matrix);
     80         src.transform(matrix, dst);
     81     }
     82 
     83     public RectF getPhotoDisplayBounds() {
     84         RectF bounds = getPhotoBounds();
     85         displayMatrix.mapRect(bounds);
     86         return bounds;
     87     }
     88 
     89     public RectF getPhotoBounds() {
     90         return (photo != null) ? new RectF(0, 0, photo.width(), photo.height()) : new RectF();
     91     }
     92 
     93     /**
     94      * Transforms display by replacing the display matrix of photo-view with the given matrix.
     95      */
     96     public void transformDisplay(Matrix matrix) {
     97         RectF bounds = getPhotoBounds();
     98         matrix.mapRect(bounds);
     99         displayMatrix.set(matrix);
    100         RectUtils.postCenterMatrix(bounds, this, displayMatrix);
    101         invalidate();
    102     }
    103 
    104     public void clipPhoto(RectF bounds) {
    105         clipBounds = bounds;
    106         invalidate();
    107     }
    108 
    109     /**
    110      * Updates the photo with animations (if any) and also updates photo display-matrix.
    111      */
    112     public void update(final Photo photo) {
    113         if (transitions == null) {
    114             setPhoto(photo);
    115             invalidate();
    116         } else if (getAnimation() != null) {
    117             // Clear old running transitions.
    118             clearTransitionAnimations();
    119             setPhoto(photo);
    120             invalidate();
    121         } else {
    122             // TODO: Use AnimationSet to chain two animations.
    123             transitions.first().setAnimationListener(new Animation.AnimationListener() {
    124 
    125                 @Override
    126                 public void onAnimationStart(Animation animation) {
    127                 }
    128 
    129                 @Override
    130                 public void onAnimationRepeat(Animation animation) {
    131                 }
    132 
    133                 @Override
    134                 public void onAnimationEnd(final Animation animation) {
    135                     post(new Runnable() {
    136 
    137                         @Override
    138                         public void run() {
    139                             if ((transitions != null) && (animation == transitions.first())) {
    140                                 startAnimation(transitions.second());
    141                             }
    142                         }
    143                     });
    144                 }
    145             });
    146             transitions.second().setAnimationListener(new Animation.AnimationListener() {
    147 
    148                 @Override
    149                 public void onAnimationStart(Animation animation) {
    150                     setPhoto(photo);
    151                 }
    152 
    153                 @Override
    154                 public void onAnimationRepeat(Animation animation) {
    155                 }
    156 
    157                 @Override
    158                 public void onAnimationEnd(Animation animation) {
    159                     post(new Runnable() {
    160 
    161                         @Override
    162                         public void run() {
    163                             clearTransitionAnimations();
    164                         }
    165                     });
    166                 }
    167             });
    168             startAnimation(transitions.first());
    169         }
    170     }
    171 
    172     private void setPhoto(Photo photo) {
    173         if (this.photo != null) {
    174             this.photo.clear();
    175             this.photo = null;
    176         }
    177         this.photo = photo;
    178 
    179         // Scale-down (if necessary) and center the photo for display.
    180         displayMatrix.reset();
    181         RectF bounds = getPhotoBounds();
    182         float scale = RectUtils.getDisplayScale(bounds, this);
    183         displayMatrix.setScale(scale, scale);
    184         displayMatrix.mapRect(bounds);
    185         RectUtils.postCenterMatrix(bounds, this, displayMatrix);
    186     }
    187 
    188     private void clearTransitionAnimations() {
    189         if (transitions != null) {
    190             transitions.first().setAnimationListener(null);
    191             transitions.second().setAnimationListener(null);
    192             transitions = null;
    193             clearAnimation();
    194         }
    195     }
    196 
    197     /**
    198      * Sets transition animations in the next update() to transit out the current bitmap and
    199      * transit in the new replacing one. Transition animations will be cleared once done.
    200      */
    201     public void setTransitionAnimations(AnimationPair transitions) {
    202         clearTransitionAnimations();
    203         this.transitions = transitions;
    204     }
    205 }
    206