Opacity change and overlap color on cross path in Android Canvas

Viewed 39

enter image description here

Hi All, I am creating a makeup application. I want to draw canvas path layers from filter. My problem is that when I tried to cross path from one to another, color opacity change. I want the effect like wherever I draw, It should be draw same layer throughout the face. Please suggest soultion to draw a single layer.

Here is my code of custom view

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

import java.util.ArrayList;

public class CustomImageView extends androidx.appcompat.widget.AppCompatImageView {
    public int width;
    public int height;
    private boolean isEditable;
    private Path mPath;
    private Paint mPaint;
    private Canvas mCanvas;
    private Bitmap canvasBitmap;
    private Boolean isZoom = false;
    private ArrayList<Path> paths = new ArrayList<>();
    private ArrayList<Path> undonePaths = new ArrayList<>();
    private ArrayList<Integer> undonePathColors = new ArrayList<>();
    private ArrayList<Integer> pathColors = new ArrayList<>();
    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;
    private PointF[] facePoints;
    private float mScaleFactor;
    int paintColor = Color.parseColor("#80ECCABC");


    Matrix mMatrix = new Matrix();
    //Matrix mCanvasMatrix = new Matrix();

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // Remember some things for zooming
    PointF last = new PointF();
    PointF start = new PointF();

    PointF[] alreadyDrawPoints;
    float minScale = 1f;
    float maxScale = 3f;
    float[] m;

    float redundantXSpace, redundantYSpace;
    static final int CLICK = 3;
    float saveScale = 1f;
    float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

    ScaleGestureDetector mScaleDetector;

    Context context;

    public CustomImageView(Context context) {
        super(context);
        sharedConstructing(context);
    }

    public CustomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupDrawing();
        sharedConstructing(context);
    }

    private void sharedConstructing(Context context) {
        super.setClickable(true);
        this.context = context;
        canvasBitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(canvasBitmap);
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        mMatrix.setTranslate(1f, 1f);
        //mCanvasMatrix.setTranslate(1f, 1f);
        m = new float[9];
        setImageMatrix(mMatrix);
        //mCanvas.setMatrix(mCanvasMatrix);
        setScaleType(ScaleType.MATRIX);

    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        if (bm != null) {
            bmWidth = bm.getWidth();
            bmHeight = bm.getHeight();
        }
    }


    public void setZoomAndDrawView(Boolean value) {
        isZoom = value;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor = (float) Math.min(
                    Math.max(.95f, detector.getScaleFactor()), 1.05);
            float origScale = saveScale;
            saveScale *= mScaleFactor;
            if (saveScale > maxScale) {
                saveScale = maxScale;
                mScaleFactor = maxScale / origScale;
            } else if (saveScale < minScale) {
                saveScale = minScale;
                mScaleFactor = minScale / origScale;
            }
            right = width * saveScale - width - (2 * redundantXSpace * saveScale);
            bottom = height * saveScale - height
                    - (2 * redundantYSpace * saveScale);
            if (origWidth * saveScale <= width || origHeight * saveScale <= height) {
                //noinspection IntegerDivisionInFloatingPointContext
                mMatrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
                //mCanvasMatrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
                if (mScaleFactor < 1) {
                    mMatrix.getValues(m);
                    //mCanvasMatrix.getValues(m);
                    float x = m[Matrix.MTRANS_X];
                    float y = m[Matrix.MTRANS_Y];
                    if (mScaleFactor < 1) {
                        if (Math.round(origWidth * saveScale) < width) {
                            if (y < -bottom) {
                                mMatrix.postTranslate(0, -(y + bottom));
                                //mCanvasMatrix.postTranslate(0, -(y + bottom));
                            } else if (y > 0) {
                                mMatrix.postTranslate(0, -y);
                                //mCanvasMatrix.postTranslate(0, -y);
                            }
                        } else {
                            if (x < -right) {
                                mMatrix.postTranslate(-(x + right), 0);
                                //mCanvasMatrix.postTranslate(-(x + right), 0);
                            } else if (x > 0) {
                                mMatrix.postTranslate(-x, 0);
                                //mCanvasMatrix.postTranslate(-x, 0);
                            }
                        }
                    }
                }
            } else {
                mMatrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(),
                        detector.getFocusY());
                //mCanvasMatrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(),
                //detector.getFocusY());
                mMatrix.getValues(m);
                //mCanvasMatrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                if (mScaleFactor < 1) {
                    if (x < -right) {
                        mMatrix.postTranslate(-(x + right), 0);
                        //mCanvasMatrix.postTranslate(-(x + right), 0);
                    } else if (x > 0) {
                        mMatrix.postTranslate(-x, 0);
                        //mCanvasMatrix.postTranslate(-x, 0);
                    }
                    if (y < -bottom) {
                        mMatrix.postTranslate(0, -(y + bottom));
                        //mCanvasMatrix.postTranslate(0, -(y + bottom));
                    } else if (y > 0) {
                        mMatrix.postTranslate(0, -y);
                        //mCanvasMatrix.postTranslate(0, -y);
                    }
                }
            }
            return true;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        // Fit to screen.
        float scale;
        float scaleX = (float) width / (float) bmWidth;
        float scaleY = (float) height / (float) bmHeight;
        scale = Math.min(scaleX, scaleY);
        mMatrix.setScale(scale, scale);
        //mCanvasMatrix.setScale(scale, scale);
        setImageMatrix(mMatrix);
        if (mCanvas != null)
            //mCanvas.setMatrix(mCanvasMatrix);
            saveScale = 1f;

        // Center the image
        redundantYSpace = (float) height - (scale * (float) bmHeight);
        redundantXSpace = (float) width - (scale * (float) bmWidth);
        redundantYSpace /= (float) 2;
        redundantXSpace /= (float) 2;

        mMatrix.postTranslate(redundantXSpace, redundantYSpace);
        //mCanvasMatrix.postTranslate(redundantXSpace, redundantYSpace);

        origWidth = width - 2 * redundantXSpace;
        origHeight = height - 2 * redundantYSpace;
        right = width * saveScale - width - (2 * redundantXSpace * saveScale);
        bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
        setImageMatrix(mMatrix);
        //if (mCanvas != null)
        //mCanvas.setMatrix(mCanvasMatrix);
    }


    public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setFacePoints(PointF[] facePoints) {
        this.facePoints = facePoints;
    }

    private PointF[] getFacePoints() {
        return facePoints;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        this.height = h;
        this.width = w;
        Log.e("ONSIZE", "ONSIZE");
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(canvasBitmap);
    }

    private void setupDrawing() {
        mPath = new Path();
        mPaint = new Paint();
        setupPaint();

    }

    public void setupPaint() {
        mPaint.setColor(paintColor);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(50);
    }


    public void reset() {
        //this.mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        //invalidate();
        undonePathColors.clear();
        pathColors.clear();
        paths.clear();
        invalidate();


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //if (isZoom) {
        //canvas.save();
        //canvas.scale(mScaleFactor, mScaleFactor);
        //}
        Log.e("onDraw", "called : " + mScaleFactor);
        for (int i = 0; i < paths.size(); i++) {
            mPaint.setColor(pathColors.get(i));
            canvas.drawPath(paths.get(i), mPaint);
        }
        //canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        canvas.drawPath(mPath, mPaint);
        //if (isZoom)
        //canvas.restore();
    }

    boolean isEnteredNoGoZone = false;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (isZoom) {

            return setupZoom(event);

        } else {
            return setupPaintt(event);

        }
    }

    private boolean setupZoom(MotionEvent event) {
        mScaleDetector.onTouchEvent(event);

        mMatrix.getValues(m);
        //mCanvasMatrix.getValues(m);
        float x = m[Matrix.MTRANS_X];
        float y = m[Matrix.MTRANS_Y];
        PointF curr = new PointF(event.getX(), event.getY());

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                last.set(event.getX(), event.getY());
                start.set(last);
                mode = DRAG;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    float deltaX = curr.x - last.x;
                    float deltaY = curr.y - last.y;
                    float scaleWidth = Math.round(origWidth * saveScale);
                    float scaleHeight = Math.round(origHeight * saveScale);
                    if (scaleWidth < width) {
                        deltaX = 0;
                        if (y + deltaY > 0)
                            deltaY = -y;
                        else if (y + deltaY < -bottom)
                            deltaY = -(y + bottom);
                    } else if (scaleHeight < height) {
                        deltaY = 0;
                        if (x + deltaX > 0)
                            deltaX = -x;
                        else if (x + deltaX < -right)
                            deltaX = -(x + right);
                    } else {
                        if (x + deltaX > 0)
                            deltaX = -x;
                        else if (x + deltaX < -right)
                            deltaX = -(x + right);

                        if (y + deltaY > 0)
                            deltaY = -y;
                        else if (y + deltaY < -bottom)
                            deltaY = -(y + bottom);
                    }
                    mMatrix.postTranslate(deltaX, deltaY);
                    //mCanvasMatrix.postTranslate(deltaX, deltaY);
                    last.set(curr.x, curr.y);
                }
                break;

            case MotionEvent.ACTION_UP:
                mode = NONE;
                int xDiff = (int) Math.abs(curr.x - start.x);
                int yDiff = (int) Math.abs(curr.y - start.y);
                if (xDiff < CLICK && yDiff < CLICK)
                    performClick();
                break;

            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                break;
        }
        setImageMatrix(mMatrix);
        if (mCanvas != null)
            //mCanvas.setMatrix(mCanvasMatrix);
            invalidate();
        return true; // indicate event was handled

    }

    private PointF[] getLeftEyebrowPath() {
        return new PointF[]{new PointF(facePoints[20].x, facePoints[20].y),
                new PointF(facePoints[21].x, facePoints[21].y),
                new PointF(facePoints[22].x, facePoints[22].y),
                new PointF(facePoints[23].x, facePoints[23].y),
                new PointF(facePoints[24].x, facePoints[24].y),
                new PointF(facePoints[25].x, facePoints[25].y)};
    }

    private PointF[] getRightEyebrowPath() {
        return new PointF[]{new PointF(facePoints[26].x, facePoints[26].y),
                new PointF(facePoints[27].x, facePoints[21].y),
                new PointF(facePoints[28].x, facePoints[28].y),
                new PointF(facePoints[29].x, facePoints[29].y),
                new PointF(facePoints[30].x, facePoints[30].y),
                new PointF(facePoints[31].x, facePoints[31].y)};
    }

    private PointF[] getLeftEyePath() {
        return new PointF[]{new PointF(facePoints[34].x, facePoints[34].y),
                new PointF(facePoints[35].x, facePoints[35].y),
                new PointF(facePoints[36].x, facePoints[36].y),
                new PointF(facePoints[37].x, facePoints[37].y),
                new PointF(facePoints[38].x, facePoints[38].y),
                new PointF(facePoints[39].x, facePoints[39].y),
                new PointF(facePoints[40].x, facePoints[40].y),
                new PointF(facePoints[41].x, facePoints[41].y)};
    }

    private PointF[] getRightEyePath() {
        return new PointF[]{new PointF(facePoints[44].x, facePoints[44].y),
                new PointF(facePoints[45].x, facePoints[45].y),
                new PointF(facePoints[46].x, facePoints[46].y),
                new PointF(facePoints[47].x, facePoints[47].y),
                new PointF(facePoints[48].x, facePoints[48].y),
                new PointF(facePoints[49].x, facePoints[49].y),
                new PointF(facePoints[50].x, facePoints[50].y),
                new PointF(facePoints[51].x, facePoints[51].y)};
    }

    private PointF[] getLipsPath() {
        return new PointF[]{new PointF(facePoints[63].x, facePoints[63].y),
                new PointF(facePoints[64].x, facePoints[64].y),
                new PointF(facePoints[65].x, facePoints[65].y),
                new PointF(facePoints[66].x, facePoints[66].y),
                new PointF(facePoints[67].x, facePoints[67].y),
                new PointF(facePoints[68].x, facePoints[68].y),
                new PointF(facePoints[69].x, facePoints[69].y),
                new PointF(facePoints[76].x, facePoints[76].y),
                new PointF(facePoints[77].x, facePoints[77].y),
                new PointF(facePoints[78].x, facePoints[78].y),
                new PointF(facePoints[79].x, facePoints[79].y),
                new PointF(facePoints[80].x, facePoints[80].y)};
    }

    private PointF[] getFacePath() {
        return new PointF[]{new PointF(facePoints[0].x, facePoints[0].y),
                new PointF(facePoints[1].x, facePoints[1].y),
                new PointF(facePoints[2].x, facePoints[2].y),
                new PointF(facePoints[3].x, facePoints[3].y),
                new PointF(facePoints[4].x, facePoints[4].y),
                new PointF(facePoints[5].x, facePoints[5].y),
                new PointF(facePoints[6].x, facePoints[6].y),
                new PointF(facePoints[7].x, facePoints[7].y),
                new PointF(facePoints[8].x, facePoints[8].y),
                new PointF(facePoints[9].x, facePoints[9].y),
                new PointF(facePoints[10].x, facePoints[10].y),
                new PointF(facePoints[11].x, facePoints[11].y),
                new PointF(facePoints[12].x, facePoints[12].y),
                new PointF(facePoints[13].x, facePoints[13].y),
                new PointF(facePoints[14].x, facePoints[14].y),
                new PointF(facePoints[15].x, facePoints[15].y),
                new PointF(facePoints[16].x, facePoints[16].y),
                new PointF(facePoints[17].x, facePoints[17].y),
                new PointF(facePoints[18].x, facePoints[18].y),
                new PointF(facePoints[19].x, facePoints[19].y),};
    }

    private boolean setupPaintt(MotionEvent event) {

        if (isEditable) {
            float touchX = event.getX();
            float touchY = event.getY();
            Matrix inverse = new Matrix();
            getImageMatrix().invert(inverse);
            float[] pts = {
                    event.getX(), event.getY()
            };
            inverse.mapPoints(pts);
            float touchUnrealX = (float) Math.floor(pts[0]);
            float touchUnrealY = (float) Math.floor(pts[1]);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.e("TAG", "Touch X, Y: " + touchUnrealX + ", " + touchUnrealX + "   Face: " + facePoints[1].x);
                    undonePaths.clear();
                    //mPath.moveTo(touchX, touchY);
                    mPath.reset();
                    mPath.moveTo(touchX, touchY);
                    mX = touchX;
                    mY = touchY;
                    //invalidate();
                    Log.e("EENND", "START");
                    break;
                case MotionEvent.ACTION_MOVE:

                    if (!PolygonUtility.isInside(getLeftEyebrowPath(), getLeftEyebrowPath().length, new PointF(touchUnrealX, touchUnrealY)) &&
                            !PolygonUtility.isInside(getRightEyebrowPath(), getRightEyebrowPath().length, new PointF(touchUnrealX, touchUnrealY)) &&
                            !PolygonUtility.isInside(getLeftEyePath(), getLeftEyePath().length, new PointF(touchUnrealX, touchUnrealY)) &&
                            !PolygonUtility.isInside(getRightEyePath(), getRightEyePath().length, new PointF(touchUnrealX, touchUnrealY)) &&
                            !PolygonUtility.isInside(getLipsPath(), getLipsPath().length, new PointF(touchUnrealX, touchUnrealY)) &&
                            PolygonUtility.isInside(getFacePath(), getFacePath().length, new PointF(touchUnrealX, touchUnrealY))) {
                        if (!isEnteredNoGoZone) {

                            mPath.lineTo(touchX, touchY);
                            float dx = Math.abs(touchX - mX);
                            float dy = Math.abs(touchY - mY);
                            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                                mPath.quadTo(mX, mY, (touchX + mX) / 2, (touchY + mY) / 2);
                                mX = touchX;
                                mY = touchY;
                            }
                        }
                    } else {
                        isEnteredNoGoZone = true;
                    }
                    //invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    //mPath.lineTo(mX, mY);


                    isEnteredNoGoZone = false;
                    mCanvas.drawPath(mPath, mPaint);
                    paths.add(mPath);
                    pathColors.add(paintColor);
                    mPath = new Path();
                    //invalidate();
                    Log.e("EENND", "END");
                    break;

                default:
                    return false;
            }
        } else {
            return false;
        }
        //postInvalidate();
        invalidate();
        return true;
    }


    public void setPaintColor(String color) {
        try {
            this.paintColor = Color.parseColor(color);
            //setUpPaint();
            /*setupDrawing();*/
            setupPaint();
        } catch (Exception ex) {

            Log.e("EEXX", ex.toString());
        }
    }

    public void setDrawingEnabled(boolean isEditable) {
        this.isEditable = isEditable;
    }


    public void undoLastStep() {
        if (paths.size() > 0) {
            undonePaths.add(paths.remove(paths.size() - 1));
            undonePathColors.add(pathColors.remove(pathColors.size() - 1));
            invalidate();
        }
    }

    public void redoLastStep() {
        if (undonePaths.size() > 0) {
            paths.add(undonePaths.remove(undonePaths.size() - 1));
            pathColors.add(undonePathColors.remove(undonePathColors.size() - 1));
            invalidate();
        }
    }
}
0 Answers
Related