Search code examples
androidandroid-canvasflood-fill

When line is draw after filling color using flood fill algorithm then filling color is gone


I have canvas drawing app. I successfully done integrating floodfill algorithm for filling color for finger drawing circle and rectangle area by finger drawing shapes. My problem is after filling color for created shapes by finger, filled color is gone when drawing line on canvas.

public class DrawingView extends View {
    private final Paint mDefaultPaint;
    private final Paint mFloodPaint = new Paint();
    Bitmap mBitmap;
    float x, y;
    ProgressDialog pd;
    LinearLayout drawing_layout;
    private Canvas mLayerCanvas = new Canvas();
    private Bitmap mLayerBitmap;
    final Point p1 = new Point();
    private Stack<DrawOp> mDrawOps = new Stack<>();
    private Stack<DrawOp> mUndoOps = new Stack<>();

    boolean isFill;/* = false;*/
    private SparseArray<DrawOp> mCurrentOps = new SparseArray<>(0);

    public DrawingView(Context context) {
        this(context, null, 0);

    }

    public DrawingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DrawingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mDefaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDefaultPaint.setStyle(Paint.Style.STROKE);
        mDefaultPaint.setStrokeJoin(Paint.Join.ROUND);
        mDefaultPaint.setStrokeCap(Paint.Cap.ROUND);
        mDefaultPaint.setStrokeWidth(40);
        mDefaultPaint.setColor(Color.GREEN);

        /*mFloodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mFloodPaint.setStyle(Paint.Style.STROKE);
        mFloodPaint.setStrokeJoin(Paint.Join.ROUND);
        mFloodPaint.setStrokeCap(Paint.Cap.ROUND);
        mFloodPaint.setStrokeWidth(40);
        mFloodPaint.setColor(Color.GREEN);*/

        setFocusable(true);
        setFocusableInTouchMode(true);
        setBackgroundColor(Color.WHITE);

        setLayerType(LAYER_TYPE_SOFTWARE, null);
        setSaveEnabled(true);
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        final int pointerCount = MotionEventCompat.getPointerCount(event);

        switch (MotionEventCompat.getActionMasked(event)) {
        case MotionEvent.ACTION_DOWN: {
            if (isFill == true) {

                int xx = (int) event.getX();
                int yy = (int) event.getY();
                Point pp = new Point(xx, yy);

                /*Point pp = new Point();
                pp.x = (int) event.getX();
                pp.y = (int) event.getY();*/

                final int sourceColor = mLayerBitmap.getPixel(xx, yy);
                final int targetColor = mDefaultPaint.getColor();
//              final int targetColor = mFloodPaint.getColor();

                new TheTask(mLayerBitmap, pp, sourceColor, targetColor)
                        .execute();

//              JniBitmap.floodFill(mLayerBitmap, xx, yy, sourceColor,targetColor);

/*              FloodFill f = new FloodFill();
                f.floodFill(mLayerBitmap, pp, sourceColor, targetColor);*/

            }
            else if(isFill == false){
                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    DrawOp current = new DrawOp(mDefaultPaint);
                    current.getPath().moveTo(event.getX(), event.getY());
                    mCurrentOps.put(id, current);
                }           
            }
        }
            break;

        case MotionEvent.ACTION_MOVE: {

                if (isFill == false) {

                final int id = MotionEventCompat.getPointerId(event, 0);
                DrawOp current = mCurrentOps.get(id);
                final int historySize = event.getHistorySize();
                for (int h = 0; h < historySize; h++) {
                    x = event.getHistoricalX(h);
                    y = event.getHistoricalY(h);
                    current.getPath().lineTo(x, y);
                }
                x = MotionEventCompat.getX(event, 0);
                y = MotionEventCompat.getY(event, 0);
                current.getPath().lineTo(x, y);
            }
        }
            break;

        case MotionEvent.ACTION_UP: {
            if(isFill == false){
//              mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
            for (int p = 0; p < pointerCount; p++) {
                final int id = MotionEventCompat.getPointerId(event, p);
                mDrawOps.push(mCurrentOps.get(id));
                mCurrentOps.remove(id);
            }
            updateLayer();
            }
        }
        /*  else{
                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    mDrawOps.push(mCurrentOps.get(id));
                    mCurrentOps.remove(id);
                }
            }*/
//      }
            break;

        case MotionEvent.ACTION_CANCEL: {
            if(isFill == false){
            for (int p = 0; p < pointerCount; p++) {
                mCurrentOps.remove(MotionEventCompat.getPointerId(event, p));
            }
            updateLayer();
        }
        }
            break;

        default:
            return false;
        }

        invalidate();
        return true;
    }

    class TheTask extends AsyncTask<Void, Integer, Void> {

        Bitmap bmp;
        Point pt;
        int replacementColor, targetColor;

        public TheTask(Bitmap bm, Point p, int sc, int tc) {
//          this.bmp = bm;
            mLayerBitmap = bm;
            pd = new ProgressDialog(getContext());
            this.pt = p;
            this.replacementColor = tc;
            this.targetColor = sc;
            pd.setMessage("Filling....");
            pd.show();
        }

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
        }

        @Override
        protected Void doInBackground(Void... params) {
            FloodFill f = new FloodFill();
//           mLayerBitmap = f.floodFill(bmp, pt, targetColor, replacementColor);
            f.floodFill(mLayerBitmap, pt, targetColor, replacementColor);

//          New Commented Algorithm
//          f.FloodFill(mLayerBitmap, pt, targetColor, replacementColor);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            pd.dismiss();
            invalidate();
            isFill = false;
        }
    }

    public void fillShapeColor(Bitmap mBitmap2) {
        isFill = true;
    }

    /*public void fillShapeColor() {
        isFill = true;
    }*/

    public void setDrawing() {
        isFill = false;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
        mLayerBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mLayerCanvas.setBitmap(mLayerBitmap);
        updateLayer();
    }

/*  private void updateDLayer(){
        mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        for (DrawOp drawOp : mDrawOps) {
            if (drawOp != null) {
//              drawOp.draw(new Canvas(mLayerBitmap));
                drawOp.draw(mLayerCanvas);
            }
        }
        invalidate();
    }*/

    private void updateLayer() {
/*      isFill=false;
        if(isFill==false){
            mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        else{
            System.out.println("Not using mLayerCanvas.drawColor()");
        }*/
//      mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
//      this.getPaintStrokeWidth();
//      this.getPaintMaskFilter();

/*      if(isFill == false){
//      this.getPaintOpacity();
            mLayerCanvas.save();
//          mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
            mLayerCanvas.restore();
        }*/


        /*mDefaultPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
        mLayerCanvas.drawPaint(mDefaultPaint);
        mDefaultPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
        mLayerCanvas.drawPaint(mDefaultPaint);*/

/*      if(isFill==false){
            mLayerCanvas.save();
            mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
            mLayerCanvas.restore();
            invalidate();   
        }
        else{
            mLayerCanvas.save();
            for (DrawOp drawOp : mDrawOps) {
                if (drawOp != null) {
//                  drawOp.draw(new Canvas(mLayerBitmap));
                    drawOp.draw(mLayerCanvas);
                }
            }
            mLayerCanvas.restore();
        }*/

        /*DrawOp dr = new DrawOp(mDefaultPaint);
        if(dr.getPath().isEmpty()){
//          isFill = true;
            isFill = false;

        }
        else{
            isFill=true;
            mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }*/

        if(isFill == false){
            mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }

        for (DrawOp drawOp : mDrawOps) {
            if (drawOp != null) {
//              drawOp.draw(new Canvas(mLayerBitmap));
                drawOp.draw(mLayerCanvas);
            }
        }
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (isInEditMode()) {
            return;
        }

      /*int w = canvas.getWidth();
        int h = canvas.getHeight();
        canvas.drawRect(0,0,w,h, mDefaultPaint);*/
        canvas.drawBitmap(mLayerBitmap, 0, 0, null);

        for (int i = 0; i < mCurrentOps.size(); i++) {
            DrawOp current = mCurrentOps.valueAt(i);
            if (current != null) {
                current.draw(canvas);
            }
        }
    }

    public void operationClear() {
        mDrawOps.clear();
        mUndoOps.clear();
        mCurrentOps.clear();
        // To Clear Whole Canvas
        mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        updateLayer();
    }

    public void operationUndo() {
        if (mDrawOps.size() > 0) {
            mUndoOps.push(mDrawOps.pop());
            updateLayer();
        }
    }

    public void operationRedo() {
        if (mUndoOps.size() > 0) {
            mDrawOps.push(mUndoOps.pop());
            updateLayer();
        }
    }

    public void setPaintStrokeWidth(float widthPx) {
        mDefaultPaint.setStrokeWidth(widthPx);
//      mFloodPaint.setStrokeWidth(widthPx);
    }

    /*public float getPaintStrokeWidth(){
        return mDefaultPaint.getStrokeWidth();

    }*/

    public void setPaintOpacity(int percent) {
        int alphaValue = (int) Math.round(percent * (255.0 / 100.0));
        mDefaultPaint.setColor(combineAlpha(mDefaultPaint.getColor(),
                alphaValue));

//      mFloodPaint.setColor(combineAlpha(mFloodPaint.getColor(),
//              alphaValue));
    }

    /*public int getPaintOpacity(){
        this.setPaintOpacity(50);
        return mDefaultPaint.getColor();
    }*/

    public void setPaintColor(String color) {
        mDefaultPaint.setXfermode(null);
        mDefaultPaint.setColor(combineAlpha(Color.parseColor(color),
                mDefaultPaint.getAlpha()));
//      mDefaultPaint.setColor(mDefaultPaint.getAlpha());

        mFloodPaint.setXfermode(null);
        mFloodPaint.setColor(combineAlpha(Color.parseColor(color),
                mFloodPaint.getAlpha()));
    }

    public void setPaintColor(int color) {
        mDefaultPaint.setXfermode(null);
        mDefaultPaint.setColor(combineAlpha(color, mDefaultPaint.getAlpha()));

        mFloodPaint.setXfermode(null);
        mFloodPaint.setColor(combineAlpha(color, mFloodPaint.getAlpha()));
    }

//  New Created
    public void setEraser(int color){
//      mDefaultPaint.setAlpha(0xFF);
        mDefaultPaint.setColor(color);
        mDefaultPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    }

    public void setPaintMaskFilter(MaskFilter filter) {
        mDefaultPaint.setMaskFilter(filter);

//      mFloodPaint.setMaskFilter(filter);
    }

    /*public MaskFilter getPaintMaskFilter(){
        return mDefaultPaint.getMaskFilter();

    }*/

    public void setPaintShader(BitmapShader shader) {
        mDefaultPaint.setShader(shader);

//      mFloodPaint.setShader(shader);
    }

    public void setPaintColorFilter(ColorFilter colorFilter) {
        mDefaultPaint.setColorFilter(colorFilter);

//      mFloodPaint.setColorFilter(colorFilter);
    }

    private static int combineAlpha(int color, int alpha) {
        return (color & 0x00FFFFFF) | ((alpha & 0xFF) << 24);
    }

    private static class DrawOp {
        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private final Path mPath = new Path();

        public DrawOp(Paint paint) {
            reset(paint);
        }

        void reset(Paint paint) {
            mPath.reset();
            update(paint);
        }

        void update(Paint paint) {
            mPaint.set(paint);
        }

        void draw(Canvas canvas) {
            canvas.drawPath(mPath, mPaint);
        }

        public Path getPath() {
            return mPath;
        }
    }

      public void setFillColor(boolean flag){
            this.isFill = flag;
        }
}

Solution

  • update your updateLayer() method like below:

    private void updateLayer() {
            /*if(isFill == false){
                mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }*/
    
            for (DrawOp drawOp : mDrawOps) {
                if (drawOp != null) {
                    drawOp.draw(mCanvas);
                }
            }
            invalidate();
        }