user2350138
user2350138

Reputation: 525

How to set Paint only image shape using Canvas

I am doing a painting app in android. I am able to show a picture on canvas and able to paint. I need to paint only with in the image like the apps in market colorfy, Coloring Bunny, Happy Zoo like that. I am not sure exactly how to acheive this task. Need your suggestions.

Here is my code.

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
android:background="#FFF">

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:src="@drawable/bug"/>

<code.android.com.kidscolorbook.DrawingPad
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawingPad"
    android:textColor="#FFFFFF"
    android:cursorVisible="true"
    />
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/btnClear"
    android:layout_gravity="bottom|center"
    android:text="Clear Canvas"/>

MainActivity.java

public class MainActivity extends AppCompatActivity {

DrawingPad drawingPad;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Settings.System.putInt(getBaseContext().getContentResolver(),
            "show_touches", 1);
    drawingPad = (DrawingPad) findViewById(R.id.drawingPad);
   // drawingPad.setBackgroundResource(R.drawable.bug);
    Button btnClear = (Button) findViewById(R.id.btnClear);
    btnClear.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            drawingPad.clearCanvas();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
protected void onPause() {
    super.onPause();
    Settings.System.putInt(getBaseContext().getContentResolver(),
            "show_touches", 0);

}}

Drawingpad.java

public class DrawingPad extends View {

private Context mContext;
private Path mPath;
private Paint mPaint;

private Bitmap mBitmap;
private Canvas mCanvas;

private float mX,mY;
private static final float TOLERANCE = 5;

public DrawingPad(Context context, AttributeSet attrs) {
    super(context, attrs);

    this.mContext = context;
    mPath = new Path();
    mPaint = new Paint();

    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeWidth(50f);


}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    Resources resources = getResources();
    mBitmap = BitmapFactory.decodeResource(resources, R.drawable.circle);
    mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath, mPaint);

}


@Override
public boolean onTouchEvent(MotionEvent event) {

    float x = event.getX();
    float y = event.getY();


    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN :
            startTouch(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            moveTouch(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            upTouch(mX,mY);
            invalidate();
            break;

    }

    return true;
}

private void startTouch(float x,float y){
    mPath.moveTo(x,y);
    mX = x;
    mY = y;
}

private void moveTouch(float x,float y)
{
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);
    System.out.print("******"+x+y);
    if(dx >= TOLERANCE || dy>= TOLERANCE){
        mPath.quadTo(mX,mY,(x+mX)/2,(y+mY)/2);

        mX = x;
        mY = y;
    }
}

private void upTouch(float x, float y)
{
    mPath.lineTo(x,y);
}

public void clearCanvas()
{
    mPath.reset();
    invalidate();
}}

My output :

enter image description here

I need to paint only with in the image and at specific shapes without spreading color.(i am aware that we can reduce stroke width) But if you can look at the above mentioned apps you may understand issue exactly.

Need your suggestions like how can we achieve that kind of feature.

Thanks in advance.

Upvotes: 1

Views: 872

Answers (1)

Mina Fawzy
Mina Fawzy

Reputation: 21452

I have the same issue , after long research I figure part of solution FloodFill Pattern ? what that mean : this mean when you click at any point at image have the same color(targetColor) --> in our case white color , it convert to (replacementColor) --> whatever your paint color in your case mPaint has red color , here is the code you will may be need to make some changes

@Override
public boolean onTouchEvent(MotionEvent event) {

    float x = event.getX();
    float y = event.getY();


    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN :
            startTouch(x, y);
            int sourceColor = bgImg.getPixel((int) x, (int) y);
            int desColor = paint.getColor();
            // pass the bitmap you want paint
            FloodFill(  mBitmap , new Point((int) x, (int) y) , sourceColor , desColor  );
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            moveTouch(x, y);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            upTouch(mX,mY);
            invalidate();
            break;

    }

    return true;
}



private void FloodFill(Bitmap bmp, Point pt, int targetColor, int replacementColor) {
        Queue<Point> q = new LinkedList<Point>();
        q.add(pt);
        while (q.size() > 0) {
            Point n = q.poll();
            if (bmp.getPixel(n.x, n.y) != targetColor)
                continue;

            Point w = n, e = new Point(n.x + 1, n.y);
            while ((w.x > 0) && (bmp.getPixel(w.x, w.y) == targetColor)) {
                bmp.setPixel(w.x, w.y, replacementColor);
                if ((w.y > 0) && (bmp.getPixel(w.x, w.y - 1) == targetColor))
                    q.add(new Point(w.x, w.y - 1));
                if ((w.y < bmp.getHeight() - 1)
                        && (bmp.getPixel(w.x, w.y + 1) == targetColor))
                    q.add(new Point(w.x, w.y + 1));
                w.x--;
            }
            while ((e.x < bmp.getWidth() - 1)
                    && (bmp.getPixel(e.x, e.y) == targetColor)) {
                bmp.setPixel(e.x, e.y, replacementColor);

                if ((e.y > 0) && (bmp.getPixel(e.x, e.y - 1) == targetColor))
                    q.add(new Point(e.x, e.y - 1));
                if ((e.y < bmp.getHeight() - 1)
                        && (bmp.getPixel(e.x, e.y + 1) == targetColor))
                    q.add(new Point(e.x, e.y + 1));
                e.x++;
            }
        }
    }

Upvotes: 1

Related Questions