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