Pincel personalizado con operación Deshacer / Rehacer en Android Canvas

Quiero implementar una aplicación de dibujo de lienzo con pincel personalizado y operación de deshacer / rehacer. En primer lugar, mi código funciona perfectamente sin usar el pincel personalizado (incluida la operación de deshacer / rehacer). De acuerdo con esta respuesta¿Cómo hacer un pincel personalizado para el lienzo en Android? Utilicé picos de imagen simples para dibujar mapas de bits.

Ahora el problema es,

La operación de deshacer, rehacer no funciona, las pinturas de pincel personalizadas una y otra vez cada vez que se mueven los puntos de contacto.

P: ¿Cómo hacer que la operación de deshacer / rehacer funcione?

Los cepilladores personalizados no son suaves como deberían. En este momento se ven ásperos y artificiales.

P. ¿Cómo hacer que la pintura sea suave y natural con pinceladas personalizadas?

Mira mi código de muestra aquí,

public class DrawingView extends View {

    private Context ctx;

    private ArrayList<Path> paths = new ArrayList<Path>();
    private ArrayList<Path> undonePaths = new ArrayList<Path>();

    private Map<Path, Float> brushMap = new HashMap<Path, Float>();
    private Map<Path, List<Vector2>> customBrushMap = new HashMap<Path, List<Vector2>>();

    private Bitmap mBitmapBrush;
    private Vector2 mBitmapBrushDimensions;
    private List<Vector2> mPositions = new ArrayList<Vector2>(100);
    private boolean isCustomBrush = false;

    private int selectedColor;
    private float brushSize, lastBrushSize;

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private Path drawPath;
    private Paint drawPaint, canvasPaint;
    private int paintColor = 0xFF660000, paintAlpha = 255;
    private Canvas drawCanvas;
    private Bitmap canvasBitmap;

    private static final class Vector2 {
        public Vector2(float x, float y) {
            this.x = x;
            this.y = y;
        }

        public final float x;
        public final float y;
    }

    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ctx = context;
        setupDrawing();
    }

    private void setupDrawing() {
        brushSize = getResources().getInteger(R.integer.small_size);
        lastBrushSize = brushSize;

        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setDither(true);
        drawPaint.setStrokeWidth(brushSize);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    private void touch_start(float x, float y) {
        undonePaths.clear();
        drawPath.reset();
        drawPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
        customBrushMap.put(drawPath, mPositions);
    }

    private void touch_up() {
        drawPath.lineTo(mX, mY);
        drawCanvas.drawPath(drawPath, drawPaint);
        paths.add(drawPath);
        brushMap.put(drawPath, brushSize);
        drawPath = new Path();
        drawPath.reset();
        invalidate();
    }

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        //detect user touch
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;

            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                if (isCustomBrush) {
                mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
                }
                touch_move(x, y);
                invalidate();
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }

        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        for (Path p : paths) {

        drawPaint.setColor(colorsMap.get(p));
        drawPaint.setShader(shaderMap.get(p));
        drawPaint.setStrokeWidth(brushMap.get(p));
        drawPaint.setAlpha(opacityMap.get(p));

        if (isCustomBrush) {
            if (customBrushMap.get(p) != null) {
                for (Vector2 pos : customBrushMap.get(p)) {
                    Paint paint = new Paint();
                    ColorFilter filter = new PorterDuffColorFilter(selectedColor, PorterDuff.Mode.SRC_IN);
                    paint.setColorFilter(filter);
                    canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, paint);
                }
            }
        } else {
            canvas.drawPath(p, drawPaint);
            drawPaint.setColor(selectedColor);
            drawPaint.setStrokeWidth(brushSize);
            canvas.drawPath(drawPath, drawPaint);
        }
    }
        canvas.restore();
    }

    public void setCustomBrush(Activity activity, String customBrush) {
        isCustomBrush = true;
        invalidate();
        int patternID = getResources().getIdentifier(customBrush, "drawable", "com.androidapp.drawingstutorial");
        mBitmapBrush = BitmapFactory.decodeResource(getResources(), patternID);
        mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
    }
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta