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();
}
}
}
