Home | History | Annotate | Download | only in actions
      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.gallery3d.photoeditor.actions;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Canvas;
     22 import android.graphics.Color;
     23 import android.graphics.Matrix;
     24 import android.graphics.Paint;
     25 import android.graphics.Path;
     26 import android.graphics.PointF;
     27 import android.graphics.RectF;
     28 import android.util.AttributeSet;
     29 import android.view.MotionEvent;
     30 
     31 /**
     32  * A view that tracks touch motions as paths and paints them as doodles.
     33  */
     34 class DoodleView extends FullscreenToolView {
     35 
     36     /**
     37      * Listener of doodle paths.
     38      */
     39     public interface OnDoodleChangeListener {
     40 
     41         void onDoodleInPhotoBounds();
     42 
     43         void onDoodleFinished(Path path, int color);
     44     }
     45 
     46     private final Path normalizedPath = new Path();
     47     private final Path drawingPath = new Path();
     48     private final Paint doodlePaint = new DoodlePaint();
     49     private final Paint bitmapPaint = new Paint(Paint.DITHER_FLAG);
     50     private final PointF lastPoint = new PointF();
     51     private final Matrix pathMatrix = new Matrix();
     52     private final Matrix displayMatrix = new Matrix();
     53 
     54     private Bitmap bitmap;
     55     private Canvas bitmapCanvas;
     56     private OnDoodleChangeListener listener;
     57 
     58     public DoodleView(Context context, AttributeSet attrs) {
     59         super(context, attrs);
     60     }
     61 
     62     public void setOnDoodleChangeListener(OnDoodleChangeListener listener) {
     63         this.listener = listener;
     64     }
     65 
     66     @Override
     67     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     68         super.onSizeChanged(w, h, oldw, oldh);
     69 
     70         RectF r = new RectF(0, 0, getPhotoWidth(), getPhotoHeight());
     71         if ((bitmap == null) && !r.isEmpty()) {
     72             bitmap = Bitmap.createBitmap((int) r.width(), (int) r.height(),
     73                     Bitmap.Config.ARGB_8888);
     74             bitmap.eraseColor(0x00000000);
     75             bitmapCanvas = new Canvas(bitmap);
     76 
     77             // Set up a matrix that maps back normalized paths to be drawn on the bitmap or canvas.
     78             pathMatrix.setRectToRect(new RectF(0, 0, 1, 1), r, Matrix.ScaleToFit.FILL);
     79         }
     80         displayMatrix.setRectToRect(r, displayBounds, Matrix.ScaleToFit.FILL);
     81     }
     82 
     83     private void drawDoodle(Canvas canvas) {
     84         if ((canvas != null) && !normalizedPath.isEmpty()) {
     85             drawingPath.set(normalizedPath);
     86             drawingPath.transform(pathMatrix);
     87             canvas.drawPath(drawingPath, doodlePaint);
     88         }
     89     }
     90 
     91     public void setColor(int color) {
     92         // Reset path to draw in a new color.
     93         finishCurrentPath();
     94         normalizedPath.moveTo(lastPoint.x, lastPoint.y);
     95         doodlePaint.setColor(Color.argb(192, Color.red(color), Color.green(color),
     96                 Color.blue(color)));
     97     }
     98 
     99     private void finishCurrentPath() {
    100         if (!normalizedPath.isEmpty()) {
    101             // Update the finished path to the bitmap.
    102             drawDoodle(bitmapCanvas);
    103             if (listener != null) {
    104                 listener.onDoodleFinished(new Path(normalizedPath), doodlePaint.getColor());
    105             }
    106             normalizedPath.rewind();
    107             invalidate();
    108         }
    109     }
    110 
    111     private void checkCurrentPathInBounds() {
    112         if ((listener != null) && !normalizedPath.isEmpty()) {
    113             RectF r = new RectF();
    114             normalizedPath.computeBounds(r, false);
    115             if (r.intersects(0, 0, 1, 1)) {
    116                 listener.onDoodleInPhotoBounds();
    117             }
    118         }
    119     }
    120 
    121     @Override
    122     public boolean onTouchEvent(MotionEvent event) {
    123         super.onTouchEvent(event);
    124 
    125         if (isEnabled()) {
    126             float x = event.getX();
    127             float y = event.getY();
    128 
    129             switch (event.getAction()) {
    130                 case MotionEvent.ACTION_DOWN:
    131                     mapPhotoPoint(x, y, lastPoint);
    132                     normalizedPath.moveTo(lastPoint.x, lastPoint.y);
    133                     break;
    134 
    135                 case MotionEvent.ACTION_MOVE:
    136                     float lastX = lastPoint.x;
    137                     float lastY = lastPoint.y;
    138                     mapPhotoPoint(x, y, lastPoint);
    139                     normalizedPath.quadTo(lastX, lastY, (lastX + lastPoint.x) / 2,
    140                             (lastY + lastPoint.y) / 2);
    141                     checkCurrentPathInBounds();
    142                     invalidate();
    143                     break;
    144 
    145                 case MotionEvent.ACTION_CANCEL:
    146                 case MotionEvent.ACTION_UP:
    147                     // Line to last position with offset to draw at least dots for single clicks.
    148                     mapPhotoPoint(x + 1, y + 1, lastPoint);
    149                     normalizedPath.lineTo(lastPoint.x, lastPoint.y);
    150                     checkCurrentPathInBounds();
    151                     finishCurrentPath();
    152                     break;
    153             }
    154         }
    155         return true;
    156     }
    157 
    158     @Override
    159     protected void onDraw(Canvas canvas) {
    160         super.onDraw(canvas);
    161 
    162         canvas.save();
    163         canvas.clipRect(displayBounds);
    164         canvas.concat(displayMatrix);
    165         if (bitmap != null) {
    166             canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
    167         }
    168         drawDoodle(canvas);
    169         canvas.restore();
    170     }
    171 }
    172