16a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang/* 26a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Copyright (C) 2010 The Android Open Source Project 36a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * 46a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Licensed under the Apache License, Version 2.0 (the "License"); 56a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * you may not use this file except in compliance with the License. 66a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * You may obtain a copy of the License at 76a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * 86a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * http://www.apache.org/licenses/LICENSE-2.0 96a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * 106a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Unless required by applicable law or agreed to in writing, software 116a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * distributed under the License is distributed on an "AS IS" BASIS, 126a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * See the License for the specific language governing permissions and 146a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * limitations under the License. 156a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang */ 166a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 176a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangpackage com.android.gallery3d.photoeditor.actions; 186a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 196a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.content.Context; 206a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.Bitmap; 216a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.Canvas; 226a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.Matrix; 236a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.Paint; 246a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.Path; 256a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.PointF; 266a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.RectF; 276a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.util.AttributeSet; 286a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.view.MotionEvent; 296a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 306a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang/** 316a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * A view that tracks touch motions as paths and paints them as doodles. 326a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang */ 336a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangclass DoodleView extends FullscreenToolView { 346a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 356a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang /** 366a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Listener of doodle paths. 376a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang */ 386a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang public interface OnDoodleChangeListener { 396a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 406a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang void onDoodleInPhotoBounds(); 416a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 42738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang void onDoodleFinished(Doodle doodle); 436a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 446a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 456a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private final Paint bitmapPaint = new Paint(Paint.DITHER_FLAG); 46738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private final Paint doodlePaint = Doodle.createPaint(); 476a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private final PointF lastPoint = new PointF(); 48738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private final Path drawingPath = new Path(); 49738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private final Matrix drawingMatrix = new Matrix(); 506a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private final Matrix displayMatrix = new Matrix(); 516a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 526a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private Bitmap bitmap; 536a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private Canvas bitmapCanvas; 54738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private Doodle doodle; 55738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private int color; 566a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private OnDoodleChangeListener listener; 576a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 586a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang public DoodleView(Context context, AttributeSet attrs) { 596a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang super(context, attrs); 606a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 616a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 626a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang public void setOnDoodleChangeListener(OnDoodleChangeListener listener) { 636a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang this.listener = listener; 646a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 656a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 666a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang @Override 676a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang protected void onSizeChanged(int w, int h, int oldw, int oldh) { 686a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang super.onSizeChanged(w, h, oldw, oldh); 696a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 706a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang RectF r = new RectF(0, 0, getPhotoWidth(), getPhotoHeight()); 716a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang if ((bitmap == null) && !r.isEmpty()) { 726a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang bitmap = Bitmap.createBitmap((int) r.width(), (int) r.height(), 736a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang Bitmap.Config.ARGB_8888); 746a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang bitmapCanvas = new Canvas(bitmap); 756a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 766a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang // Set up a matrix that maps back normalized paths to be drawn on the bitmap or canvas. 77738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang drawingMatrix.setRectToRect(new RectF(0, 0, 1, 1), r, Matrix.ScaleToFit.FILL); 786a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 796a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang displayMatrix.setRectToRect(r, displayBounds, Matrix.ScaleToFit.FILL); 806a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 816a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 826a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang private void drawDoodle(Canvas canvas) { 83738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang if ((canvas != null) && (doodle != null)) { 84738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang doodlePaint.setColor(doodle.getColor()); 85738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang doodle.getDrawingPath(drawingMatrix, drawingPath); 866a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.drawPath(drawingPath, doodlePaint); 876a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 886a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 896a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 906a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang public void setColor(int color) { 91738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang // Restart doodle to draw in a new color. 92738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang this.color = color; 93738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang finishDoodle(); 94738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang startDoodle(); 95738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang } 96738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang 97738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private void startDoodle() { 98738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang doodle = new Doodle(color, new PointF(lastPoint.x, lastPoint.y)); 996a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1006a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 101738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private void finishDoodle() { 102738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang if ((doodle != null) && !doodle.isEmpty()) { 103738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang // Update the finished non-empty doodle to the bitmap. 1046a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang drawDoodle(bitmapCanvas); 1056a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang if (listener != null) { 106738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang listener.onDoodleFinished(doodle); 1076a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1086a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang invalidate(); 1096a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 110738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang doodle = null; 1116a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1126a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 113738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang private void addLastPointIntoDoodle() { 114738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang if ((doodle != null) && doodle.addControlPoint(new PointF(lastPoint.x, lastPoint.y))) { 115738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang if (listener != null) { 1166a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang listener.onDoodleInPhotoBounds(); 1176a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 118738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang invalidate(); 1196a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1206a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1216a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1226a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang @Override 1236a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang public boolean onTouchEvent(MotionEvent event) { 1246a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang super.onTouchEvent(event); 1256a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1266a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang if (isEnabled()) { 1276a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang float x = event.getX(); 1286a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang float y = event.getY(); 1296a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1306a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang switch (event.getAction()) { 1316a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang case MotionEvent.ACTION_DOWN: 1326a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang mapPhotoPoint(x, y, lastPoint); 133738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang startDoodle(); 1346a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang break; 1356a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1366a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang case MotionEvent.ACTION_MOVE: 1376a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang mapPhotoPoint(x, y, lastPoint); 138738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang addLastPointIntoDoodle(); 1396a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang break; 1406a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1416a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang case MotionEvent.ACTION_CANCEL: 1426a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang case MotionEvent.ACTION_UP: 1436a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang // Line to last position with offset to draw at least dots for single clicks. 1446a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang mapPhotoPoint(x + 1, y + 1, lastPoint); 145738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang addLastPointIntoDoodle(); 146738e82e163f90e1fe7aeef8bd153dc7763631f30Yuli Huang finishDoodle(); 1476a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang break; 1486a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1496a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1506a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang return true; 1516a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1526a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1536a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang @Override 1546a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang protected void onDraw(Canvas canvas) { 1556a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang super.onDraw(canvas); 1566a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang 1576a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.save(); 1586a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.clipRect(displayBounds); 1596a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.concat(displayMatrix); 1606a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang if (bitmap != null) { 1616a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.drawBitmap(bitmap, 0, 0, bitmapPaint); 1626a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1636a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang drawDoodle(canvas); 1646a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang canvas.restore(); 1656a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang } 1666a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang} 167