Android How to Apply Mask on Imageview

Android how to apply mask on ImageView?

I have found the perfect combination for creating masking without black border after researching through all the stackoverflow posts. It suits my purpose quite well.

Currently I'm creating a draggable view using one normal image and a masking image (a png with transparency), so I'll need to override the onDraw function.

private Bitmap mImage = ...;
private Bitmap mMask = ...; // png mask with transparency
private int mPosX = 0;
private int mPosY = 0;

private final Paint maskPaint;
private final Paint imagePaint;

public CustomView (final Context context) {
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

imagePaint = new Paint();
imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}

/* TODO
if you have more constructors, make sure you initialize maskPaint and imagePaint
Declaring these as final means that all your constructors have to initialize them.
Failure to do so = your code won't compile.
*/

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

canvas.save();
canvas.drawBitmap(mMask, 0, 0, maskPaint);
canvas.drawBitmap(mImage, mPosX, mPosY, imagePaint);
canvas.restore();
}

Mask ImageView with round corner background

I suggest you to use another method:

One FrameLayout and two ImageView can do it.

<FrameLayout>
<ImageView /> your image
<ImageView /> put a image which has a transparent circle in it
</FrameLayout>

then your image can been seen via transparent circle.

Apply custom mask to an ImageView

Thanks for the answers, which will definitely be helpful for other users.
Unfortunetly I had to help myself to get the desired results.

This is my final solution which applies a quad bezier to the ImageView.

class HeaderImageView : AppCompatImageView {
constructor(context: Context?) : super(context) {
init()
}

constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}

lateinit var paint: Paint

private fun init() {
paint = Paint()
paint.color = Color.WHITE
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)
paint.style = Paint.Style.FILL
}

@SuppressLint("CanvasSize")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

val fHeight = canvas.height.toFloat()
val startEndHeight = canvas.height / 1.18f
val fWidth = canvas.width.toFloat()
val halfWidth = (fWidth / 2)

val path = Path()
//X = Left side, Y = close to bottom
val ptStart = PointF(0f, startEndHeight)
//X = Middle, Y = Bottom
val ptMiddle = PointF(halfWidth, fHeight + 95)
// X = Right Side, Y = close to bottom
val ptEnd = PointF(fWidth, startEndHeight)

path.moveTo(ptStart.x, ptStart.y)
path.quadTo(ptMiddle.x, ptMiddle.y, ptEnd.x, ptEnd.y)
path.lineTo(fWidth, fHeight)
path.lineTo(0f, fHeight)

path.close()

canvas.drawPath(path, paint)
}
}

Sample Image

Masking(crop) image in frame

Finally got the solution while changing mask image and using of Xfermode with Bitmap

Mask

Sample Image

 ImageView mImageView= (ImageView)findViewById(R.id.imageview_id);
Bitmap original = BitmapFactory.decodeResource(getResources(),R.drawable.content_image);
Bitmap mask = BitmapFactory.decodeResource(getResources(),R.drawable.mask);
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(original, 0, 0, null);
mCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
mImageView.setImageBitmap(result);
mImageView.setScaleType(ScaleType.CENTER);
mImageView.setBackgroundResource(R.drawable.background_frame);

see output

Sample Image

Source can be found here

How to mask/crop a rectangle in ImageView

I wrote a simple View which extends ImageView which does what you're trying to do. To work with it, all you need to do is give it a Rect which defines the zone you want as grayscale.

Here's the code:

public class MaskImageView extends ImageView
{
private Rect mMaskRect;

public MaskImageView(Context context)
{
super(context);
init();
}

public MaskImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}

public MaskImageView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}

private void init()
{
mMaskRect = new Rect();
}

// Use this to define the area which should be masked.
public void setRect(Rect rect)
{
mMaskRect.set(rect);
}

@Override
protected void onDraw(Canvas canvas)
{
canvas.clipRect(mMaskRect, Region.Op.DIFFERENCE);
super.onDraw(canvas);
}
}

I've tested it and it seems to work well. This should get you started.

Create a drag and clear mask on android image view

Here's a code sample. The important parts are 1) it uses a customized view that overlaps the image view; 2) it uses a bitmap canvas for the actual drawing (required in order to be able to clear pixels); and 3) the painting uses the special CLEAR transfer mode.

Output:

Sample Image

DrawView.java:

package com.example.drawingwithmask;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class DrawView extends View implements OnTouchListener {

private Paint bmPaint = new Paint();
private Paint drawPaint = new Paint();
private Path path = new Path();
private Canvas cv = null;
private Bitmap bm = null;
private boolean firstTimeThru = true;

public DrawView(Context context) {
super(context);
init();
}

public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public void init() {
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
}

@Override
public void onDraw(Canvas canvas) {
// Set everything up the first time anything gets drawn:
if (firstTimeThru) {
firstTimeThru = false;

// Just quickly fill the view with a black mask:
canvas.drawColor(Color.BLACK);

// Create a new bitmap and canvas and fill it with a black mask:
bm = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
cv = new Canvas();
cv.setBitmap(bm);
cv.drawColor(Color.BLACK);

// Specify that painting will be with fat strokes:
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeWidth(canvas.getWidth() / 15);

// Specify that painting will clear the pixels instead of paining new ones:
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}

cv.drawPath(path, drawPaint);
canvas.drawBitmap(bm, 0, 0, bmPaint);

super.onDraw(canvas);
}

public boolean onTouch(View view, MotionEvent event) {
float xPos = event.getX();
float yPos = event.getY();

switch (event.getAction()) {

// Set the starting position of a new line:
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos, yPos);
return true;

// Draw a line to the ending position:
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos, yPos);
break;

default:
return false;
}

// Call onDraw() to redraw the whole view:
invalidate();
return true;}
}

activity_layout:

<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher" />

<com.example.drawingwithmask.DrawView
android:id="@+id/draw"
android:layout_width="match_parent"
android:layout_height="match_parent" />

MainActivity.java:

package com.example.drawingwithmask;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
DrawView drawView = null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);

drawView = (DrawView) findViewById(R.id.draw);
}
}

Addendum:

Here's a method that can estimate if a certain threshold of mask clearing has been reached. I tested it by calling checkProgress() from the MOTION_MOVE event case. This might not be the best way or perhaps even a good way, but it does at least provide an estimate at the expense of STEPS^2 (400 as presented) pixel checks.

final int STEPS = 20;
final double THRESHOLD = 0.70;

private void checkProgress() {

int sum = 0;
for (int x = 0; x < bm.getWidth(); x += bm.getWidth() / STEPS)
for (int y = 0; y < bm.getHeight(); y += bm.getHeight() / STEPS)
if (bm.getPixel(x, y) != Color.BLACK)
sum++;

double progress = (double) sum / (STEPS * STEPS);
Log.v(TAG, "Cleared: " + progress);

if (progress >= THRESHOLD)
Log.i(TAG, "Done!");
}


Related Topics



Leave a reply



Submit