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 17package com.android.gallery3d.photoeditor.actions; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.Paint; 24import android.graphics.Path; 25import android.graphics.PointF; 26import android.graphics.RectF; 27import android.util.AttributeSet; 28import android.view.MotionEvent; 29 30/** 31 * A view that tracks touch motions as paths and paints them as doodles. 32 */ 33class DoodleView extends FullscreenToolView { 34 35 /** 36 * Listener of doodle paths. 37 */ 38 public interface OnDoodleChangeListener { 39 40 void onDoodleInPhotoBounds(); 41 42 void onDoodleFinished(Doodle doodle); 43 } 44 45 private final Paint bitmapPaint = new Paint(Paint.DITHER_FLAG); 46 private final Paint doodlePaint = Doodle.createPaint(); 47 private final PointF lastPoint = new PointF(); 48 private final Path drawingPath = new Path(); 49 private final Matrix drawingMatrix = new Matrix(); 50 private final Matrix displayMatrix = new Matrix(); 51 52 private Bitmap bitmap; 53 private Canvas bitmapCanvas; 54 private Doodle doodle; 55 private int color; 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 bitmapCanvas = new Canvas(bitmap); 75 76 // Set up a matrix that maps back normalized paths to be drawn on the bitmap or canvas. 77 drawingMatrix.setRectToRect(new RectF(0, 0, 1, 1), r, Matrix.ScaleToFit.FILL); 78 } 79 displayMatrix.setRectToRect(r, displayBounds, Matrix.ScaleToFit.FILL); 80 } 81 82 private void drawDoodle(Canvas canvas) { 83 if ((canvas != null) && (doodle != null)) { 84 doodlePaint.setColor(doodle.getColor()); 85 doodle.getDrawingPath(drawingMatrix, drawingPath); 86 canvas.drawPath(drawingPath, doodlePaint); 87 } 88 } 89 90 public void setColor(int color) { 91 // Restart doodle to draw in a new color. 92 this.color = color; 93 finishDoodle(); 94 startDoodle(); 95 } 96 97 private void startDoodle() { 98 doodle = new Doodle(color, new PointF(lastPoint.x, lastPoint.y)); 99 } 100 101 private void finishDoodle() { 102 if ((doodle != null) && !doodle.isEmpty()) { 103 // Update the finished non-empty doodle to the bitmap. 104 drawDoodle(bitmapCanvas); 105 if (listener != null) { 106 listener.onDoodleFinished(doodle); 107 } 108 invalidate(); 109 } 110 doodle = null; 111 } 112 113 private void addLastPointIntoDoodle() { 114 if ((doodle != null) && doodle.addControlPoint(new PointF(lastPoint.x, lastPoint.y))) { 115 if (listener != null) { 116 listener.onDoodleInPhotoBounds(); 117 } 118 invalidate(); 119 } 120 } 121 122 @Override 123 public boolean onTouchEvent(MotionEvent event) { 124 super.onTouchEvent(event); 125 126 if (isEnabled()) { 127 float x = event.getX(); 128 float y = event.getY(); 129 130 switch (event.getAction()) { 131 case MotionEvent.ACTION_DOWN: 132 mapPhotoPoint(x, y, lastPoint); 133 startDoodle(); 134 break; 135 136 case MotionEvent.ACTION_MOVE: 137 mapPhotoPoint(x, y, lastPoint); 138 addLastPointIntoDoodle(); 139 break; 140 141 case MotionEvent.ACTION_CANCEL: 142 case MotionEvent.ACTION_UP: 143 // Line to last position with offset to draw at least dots for single clicks. 144 mapPhotoPoint(x + 1, y + 1, lastPoint); 145 addLastPointIntoDoodle(); 146 finishDoodle(); 147 break; 148 } 149 } 150 return true; 151 } 152 153 @Override 154 protected void onDraw(Canvas canvas) { 155 super.onDraw(canvas); 156 157 canvas.save(); 158 canvas.clipRect(displayBounds); 159 canvas.concat(displayMatrix); 160 if (bitmap != null) { 161 canvas.drawBitmap(bitmap, 0, 0, bitmapPaint); 162 } 163 drawDoodle(canvas); 164 canvas.restore(); 165 } 166} 167