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