Android: How to fill color to the specific part of the Image only?

20,369

Solution 1

This is the complete code of filling color into image below :

Image for filling: http://i.stack.imgur.com/7rita.jpg

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >

<RelativeLayout
    android:id="@+id/relative_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="5dp"
    android:layout_weight="1" >

    <ImageView
        android:id="@+id/coringImage"
        android:layout_width="300dp"
        android:layout_height="300dp" />
</RelativeLayout>

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <Button
        android:id="@+id/btn_red"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="red" />

    <Button
        android:id="@+id/btn_yellow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="yellow" />

    <Button
        android:id="@+id/btn_blue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="blue" />
</LinearLayout>

</LinearLayout> 

Activity Class

public class FillColorActivity extends Activity implements OnTouchListener {
private RelativeLayout drawingLayout;
private MyView myView;
Button red, blue, yellow;
Paint paint;

/** Called when the activity is first created. */
/*
 * 
 * private ImageView imageView; private Canvas cv; private Bitmap mask,
 * original, colored; private int r,g,b; private int sG, sR, sB;
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    myView = new MyView(this);
    drawingLayout = (RelativeLayout) findViewById(R.id.relative_layout);
    drawingLayout.addView(myView);

    red = (Button) findViewById(R.id.btn_red);
    blue = (Button) findViewById(R.id.btn_blue);
    yellow = (Button) findViewById(R.id.btn_yellow);

    red.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            paint.setColor(Color.RED);
        }
    });

    yellow.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            paint.setColor(Color.YELLOW);
        }
    });
    blue.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            paint.setColor(Color.BLUE);
        }
    });
}

public class MyView extends View {

    private Path path;
    Bitmap mBitmap;
    ProgressDialog pd;
    final Point p1 = new Point();
    Canvas canvas;

    // Bitmap mutableBitmap ;
    public MyView(Context context) {
        super(context);

        paint = new Paint();
        paint.setAntiAlias(true);
        pd = new ProgressDialog(context);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(5f);
        mBitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.cartoon).copy(Bitmap.Config.ARGB_8888, true);

        this.path = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        this.canvas = canvas;
        paint.setColor(Color.GREEN);
        canvas.drawBitmap(mBitmap, 0, 0, paint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            p1.x = (int) x;
            p1.y = (int) y;
            final int sourceColor = mBitmap.getPixel((int) x, (int) y);
            final int targetColor = paint.getColor();
            new TheTask(mBitmap, p1, sourceColor, targetColor).execute();
            invalidate();
        }
        return true;
    }

    public void clear() {
        path.reset();
        invalidate();
    }

    public int getCurrentPaintColor() {
        return paint.getColor();
    }

    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;
            this.pt = p;
            this.replacementColor = tc;
            this.targetColor = sc;
            pd.setMessage("Filling....");
            pd.show();
        }

        @Override
        protected void onPreExecute() {
            pd.show();

        }

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

        }

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

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

// flood fill

public class FloodFill {
    public void floodFill(Bitmap image, Point node, int targetColor,
            int replacementColor) {
        int width = image.getWidth();
        int height = image.getHeight();
        int target = targetColor;
        int replacement = replacementColor;
        if (target != replacement) {
            Queue<Point> queue = new LinkedList<Point>();
            do {

                int x = node.x;
                int y = node.y;
                while (x > 0 && image.getPixel(x - 1, y) == target) {
                    x--;

                }
                boolean spanUp = false;
                boolean spanDown = false;
                while (x < width && image.getPixel(x, y) == target) {
                    image.setPixel(x, y, replacement);
                    if (!spanUp && y > 0
                            && image.getPixel(x, y - 1) == target) {
                        queue.add(new Point(x, y - 1));
                        spanUp = true;
                    } else if (spanUp && y > 0
                            && image.getPixel(x, y - 1) != target) {
                        spanUp = false;
                    }
                    if (!spanDown && y < height - 1
                            && image.getPixel(x, y + 1) == target) {
                        queue.add(new Point(x, y + 1));
                        spanDown = true;
                    } else if (spanDown && y < height - 1
                            && image.getPixel(x, y + 1) != target) {
                        spanDown = false;
                    }
                    x++;
                }
        } while ((node = queue.poll()) != null);
        }
    }
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    return false;
}
}

Solution 2

Use FloodFill.

  FloodFill f= new FloodFill();
        f.floodFill(bmp,pt,targetColor,replacementColor);

  public class FloodFill {
public void floodFill(Bitmap  image, Point node, int targetColor,
        int replacementColor) {
    int width = image.getWidth();
    int height = image.getHeight();
    int target = targetColor;
    int replacement = replacementColor;
    if (target != replacement) {
        Queue<Point> queue = new LinkedList<Point>();
        do {
            int x = node.x;
            int y = node.y;
            while (x > 0 && image.getPixel(x - 1, y) == target) {
                x--;
            }
            boolean spanUp = false;
            boolean spanDown = false;
            while (x < width && image.getPixel(x, y) == target) {
                image.setPixel(x, y, replacement);
                if (!spanUp && y > 0 && image.getPixel(x, y - 1) == target) {
                    queue.add(new Point(x, y - 1));
                    spanUp = true;
                } else if (spanUp && y > 0
                        && image.getPixel(x, y - 1) != target) {
                    spanUp = false;
                }
                if (!spanDown && y < height - 1
                        && image.getPixel(x, y + 1) == target) {
                    queue.add(new Point(x, y + 1));
                    spanDown = true;
                } else if (spanDown && y < height - 1
                        && image.getPixel(x, y + 1) != target) {
                    spanDown = false;
                }
                x++;
            }
        } while ((node = queue.poll()) != null);
    }
}
}

Solution 3

I would suspect the initial bitmap was created in read-only mode. That is probably what cause the exception on setPixel(). You can change that via BitmapFactory options:

opt = new BitmapFactory.Options();
// force RGBA pixel format even for 8-bit grey scale image files.
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
// want to modify the bitmap content.
opt.inMutable = true;
Bitmap bitmap=BitmapFactory.decodeFile("clown.png", opt);
Share:
20,369
Shreyash Mahajan
Author by

Shreyash Mahajan

About Me https://about.me/sbm_mahajan Email [email protected] [email protected] Mobile +919825056129 Skype sbm_mahajan

Updated on July 28, 2022

Comments

  • Shreyash Mahajan
    Shreyash Mahajan over 1 year

    I have Image Like this:

    enter image description here Now, i want to fill the color to the Specific part of that image. as like If i select color blue and if i touch on Cap then the cap should be fill with the color Blue. Same thing should be also happen with the other part like nose, Mouth, Eyes etc

    So, How it is possible using android ?

    Can any budy help me please.

    Updated

    I have try with the implementation of the FloodFill algorithm in my app. See Here

    But after doing that i got exception like:

        03-09 17:45:16.260: ERROR/AndroidRuntime(2558): java.lang.IllegalStateException
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.graphics.Bitmap.setPixel(Bitmap.java:847)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.project.fingerpaint.FinderPaintDemo.FloodFill(FinderPaintDemo.java:284)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.project.fingerpaint.FinderPaintDemo.access$3(FinderPaintDemo.java:272)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.project.fingerpaint.FinderPaintDemo$MyView.onTouchEvent(FinderPaintDemo.java:187)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.View.dispatchTouchEvent(View.java:3766)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1671)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1655)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.os.Handler.dispatchMessage(Handler.java:99)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.os.Looper.loop(Looper.java:123)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at android.app.ActivityThread.main(ActivityThread.java:4627)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at java.lang.reflect.Method.invokeNative(Native Method)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at java.lang.reflect.Method.invoke(Method.java:521)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    03-09 17:45:16.260: ERROR/AndroidRuntime(2558):     at dalvik.system.NativeStart.main(Native Method)
    

    Now, What wrong in my code ??

    Please help me in that condition.

    Thanks.

  • Ramakrishna
    Ramakrishna over 11 years
    @Raghuandan K how we can get the particular bitmap and point from selected area. Can you explain it.
  • Raghunandan
    Raghunandan over 11 years
    final Point p1 = new Point(); p1.x=(int) x; p1.y=(int) y; final int sourceColor= mDrawingManager.mDrawingUtilities.mBitmap.getPixel((int)x,(‌​int) y); final int targetColor = mDrawingManager.mDrawingUtilities.mPaint.getColor(); new TheTask(mDrawingManager.mDrawingUtilities.mBitmap, p1, sourceColor, targetColor).execute();
  • Ramakrishna
    Ramakrishna over 11 years
    Okay, thanks let me try and get back to you.
  • Raghunandan
    Raghunandan over 11 years
    Here x and y are co-ordinates where user touches on the screen.
  • Raghunandan
    Raghunandan over 11 years
    mDrawingUtilities.mCanvas = new Canvas(mDrawingUtilities.mBitmap);
  • Raghunandan
    Raghunandan over 11 years
    @Ramakrishna did it solve your problem.
  • Ramakrishna
    Ramakrishna over 11 years
    No getting out of memory exception. Why?
  • Raghunandan
    Raghunandan over 11 years
  • Pankaj Lilan
    Pankaj Lilan over 7 years
    @Raghunandan I am getting IllegalStateException in Flood Fill image.setPixel(x, y, replacement); as I am trying to add bitmap from sdcard. So what would be the possible solution for this?
  • isuru
    isuru about 7 years
    If I use SVG instead of normal image, what will be the approach?
  • Raghunandan
    Raghunandan about 7 years
    @isuru not sure as i have not done this. But can you convert vectordrawable to Bitmap and then apply floodfill stackoverflow.com/questions/33696488/…. Check if it works
  • Zia Ur Rahman
    Zia Ur Rahman about 6 years
    Does not fill the area when I tap on Image. code is below. can you please implement it in MainActivity on Touch event to get any color and fill the area. please
  • Zia Ur Rahman
    Zia Ur Rahman about 6 years
    Brilliant! Great work. Thanks, but How can we make this filling Fast. How to add code to make it fast filling? And it leaves white spaces/dots on corners, how to fill/cover it fully. please
  • Sheetal Shinde
    Sheetal Shinde about 2 years
    @Tim Thanks!!! But there are some spaces at corner which ain't get filled..How to make it smooth to occupy full portion with color.