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.Matrix; 23 import android.graphics.Paint; 24 import android.graphics.Path; 25 import android.graphics.PointF; 26 import android.graphics.RectF; 27 import android.util.AttributeSet; 28 import android.view.MotionEvent; 29 30 /** 31 * A view that tracks touch motions as paths and paints them as doodles. 32 */ 33 class DoodleView extends FullscreenToolView { 34 35 /** 36 * Listener of doodle paths. 37 */ 38 public interface OnDoodleChangeListener { 39 40 void onDoodleChanged(Doodle doodle); 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) { 115 doodle.addControlPoint(new PointF(lastPoint.x, lastPoint.y)); 116 if (listener != null) { 117 listener.onDoodleChanged(doodle); 118 } 119 invalidate(); 120 } 121 } 122 123 @Override 124 public boolean onTouchEvent(MotionEvent event) { 125 super.onTouchEvent(event); 126 127 if (isEnabled()) { 128 float x = event.getX(); 129 float y = event.getY(); 130 131 switch (event.getAction()) { 132 case MotionEvent.ACTION_DOWN: 133 mapPhotoPoint(x, y, lastPoint); 134 startDoodle(); 135 break; 136 137 case MotionEvent.ACTION_MOVE: 138 mapPhotoPoint(x, y, lastPoint); 139 addLastPointIntoDoodle(); 140 break; 141 142 case MotionEvent.ACTION_CANCEL: 143 case MotionEvent.ACTION_UP: 144 // Line to last position with offset to draw at least dots for single clicks. 145 mapPhotoPoint(x + 1, y + 1, lastPoint); 146 addLastPointIntoDoodle(); 147 finishDoodle(); 148 break; 149 } 150 } 151 return true; 152 } 153 154 @Override 155 protected void onDraw(Canvas canvas) { 156 super.onDraw(canvas); 157 158 canvas.save(); 159 canvas.clipRect(displayBounds); 160 canvas.concat(displayMatrix); 161 if (bitmap != null) { 162 canvas.drawBitmap(bitmap, 0, 0, bitmapPaint); 163 } 164 drawDoodle(canvas); 165 canvas.restore(); 166 } 167 } 168