I want to crop image by polygon area, but couldn`t find any library, which can make it.
OpenCV is too big for this small thing. JJIL [enter link description here] crop just rectangle area.
Maybe you have any ideas how i can achieve it? Thanks for help!
FOR Nidhi:
Try something like this, if doesnot work - create another canvas for path, and than get Bitmap from it (for mask), and apply this mask bitmap to your initial canvas instead drawPath.
Bitmap obmp = BitmapFactory.decodeResource(getResources(), R.drawable.image1);
Bitmap resultImg = Bitmap.createBitmap(obmp.getWidth(), obmp.getHeight(), Bitmap.Config.ARGB_8888);
Bitmap maskImg = Bitmap.createBitmap(obmp.getWidth(), obmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(resultImg);
Canvas maskCanvas = new Canvas(maskImg);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);;
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
Path path = new Path();
path.moveTo(view.mx,view.my);
path.lineTo(view.x1,view.y1);
path.lineTo(view.x2,view.y2 );
path.lineTo(view.x3,view.y3);
path.lineTo(view.x4,view.y4);
path.close();
maskCanvas.drawPath(path, paint);
mCanvas.drawBitmap(obmp, 0, 0, null);
mCanvas.drawBitmap(maskImg, 0, 0, paint);
Thanks for Eddy_Em, i have achieved this by using PorterDuffXfermode.
Good example
This a working Kotlin example, that clips and image to a polygon share depending on the path
private fun createBitmap() {
var bitmap = BitmapFactory.decodeResource(resources, R.drawable.gr)
val mutableBitmap: Bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
val bitmap2 = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888)
val polyCanvas = Canvas(bitmap2)
val canvas = Canvas(mutableBitmap)
var paint = Paint()
paint.strokeWidth = 9f
val path = Path()
path.moveTo(150f, 0f)
path.lineTo(230f, 120f)
path.lineTo(290f, 160f)
path.lineTo(150f, 170f)
path.lineTo(70f, 200f)
path.lineTo(150f, 0f)
polyCanvas.drawPath(path, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
polyCanvas.drawBitmap(mutableBitmap, 0f, 0f, paint)
imageView.setImageBitmap(bitmap2)
}
Related
`
There is an requirement in my project to set any bitmap into mug and also edit bitmap and wright some text on image then past this bitmap to mug just like attached image .
I need a solution in Android or any supported library, Before I have tried to do this with Android Canvas and its method to draw arc But not reached my requirement.
/**
* Draw one image over other using the canvas paint and path drawing.
*/
public Bitmap createMaskedImageInImageCenterRightMug(Drawable back,
Bitmap bitmapToDrawInTheCenter) {
Bitmap backgroundBitmap = ((BitmapDrawable) back).getBitmap();
int hieghtBack = backgroundBitmap.getHeight();
int widthBack = backgroundBitmap.getWidth();
int hieghtFront = bitmapToDrawInTheCenter.getHeight();
int widthFront = bitmapToDrawInTheCenter.getWidth();
int widthToDrawOnMug = widthBack / 2;
backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, (int) canvas_width, hieghtBack, true);
// Create mask
Bitmap backgroundBitmapMask = Bitmap.createBitmap(backgroundBitmap, 7, 0, (int) (canvas_width / 2), hieghtBack);
if (widthToDrawOnMug <= widthFront) {
bitmapToDrawInTheCenter = Bitmap.createBitmap(
bitmapToDrawInTheCenter, (widthFront * 40) / 100, 0,
(widthFront * 60) / 100, hieghtFront);
}
Bitmap backgroundBitmapScaledMask = Bitmap.createScaledBitmap(
backgroundBitmapMask, widthToDrawOnMug, hieghtBack - 50, true);
bitmapToDrawInTheCenter = Bitmap.createScaledBitmap(
bitmapToDrawInTheCenter, backgroundBitmapScaledMask.getWidth(),
backgroundBitmapScaledMask.getHeight(), true);
Bitmap resultBitmap = Bitmap.createBitmap(
backgroundBitmapScaledMask.getWidth(),
backgroundBitmapScaledMask.getHeight(),
backgroundBitmapScaledMask.getConfig());
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(backgroundBitmapScaledMask, 0, 0, null);
canvas.drawBitmap(bitmapToDrawInTheCenter, 0, 0, paint);
return resultBitmap;
}
/**
* Draw one image over other using the canvas paint and path drawing.
*/
public Bitmap pasteOverMugForRightMug(Drawable back,
Bitmap bitmapToDrawInTheCenter) {
Bitmap backgroundBitmap = ((BitmapDrawable) back).getBitmap();
Bitmap resultBitmap = Bitmap.createBitmap(backgroundBitmap.getWidth(), backgroundBitmap.getHeight(), backgroundBitmap.getConfig());
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(backgroundBitmap, new Matrix(), null);
Paint paint = new Paint();
canvas.drawBitmap(bitmapToDrawInTheCenter, 0, 25, paint);
return resultBitmap;
}
Here is my code . Some hard code values are used according plain Mug image Aspect Ratio.
Also Tried to do this with Open GL but it was very complex .
And and could not find method for cylinder shape in one of the image processing library Image magic.
if any one have an idea please share .
This is what I have:
Bitmap result = Bitmap.create(600, 600, Config.ARG_8888);
Canvas canvas = new Canvas(result);
canvas.clipPath(path) // custom path is a rotated rectangle that has an offset x and y within the 600x600 rectangle
canvas.drawBitmap(sourceBitmap, 0, 0, new Paint());
canvas.save();
When I check the result bitmap, I only see the clip path which is I want, but I want the result image to be the same size as the clip path and not still 600x600.
Thanks
as i said in the comment above, you can use drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) to crop your Bitmap result to the cropped one or you can do it directly:
// get the Bitmap
Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// setup sample Path
Path path = new Path();
path.moveTo(10, 10);
path.lineTo(30, 10);
path.lineTo(30, 20);
path.close();
RectF bounds = new RectF();
path.computeBounds(bounds, true);
Rect pathBounds = new Rect();
bounds.roundOut(pathBounds);
Bitmap outBitmap = Bitmap.createBitmap(pathBounds.width(), pathBounds.height(), Bitmap.Config.ARGB_8888);
Shader shader = new BitmapShader(b, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Canvas c = new Canvas(outBitmap);
Paint paint = new Paint();
paint.setShader(shader);
c.translate(-pathBounds.left, -pathBounds.top);
c.drawPath(path, paint);
I have a mask bitmap with a half is red color and ones is transparent like this
https://www.dropbox.com/s/931ixef6myzusi0/s_2.png
I want to use mask bitmap to draw content on canvas only visible in red area, code like this:
Paint paint = new Paint();
public void draw(Canvas canvas) {
// draw content here
...
//and mask bitmap here
paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
canvas.drawBitmap(maskBitmap, 0, 0, paint);
}
The result as my expecting (content only visible in red area, BUT THE TRANSPARENT AREA BECOME BLACK IS PROBLEM!)
this image result :https://www.dropbox.com/s/mqj48992wllfkiq/s_2%20copy.png
Anyone help me???
Here is a solution which helped me to implement masking:
public void draw(Canvas canvas) {
Bitmap original = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.original_image);
Bitmap mask = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.mask_image);
//You can change original image here and draw anything you want to be masked on it.
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
Canvas tempCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
tempCanvas.drawBitmap(original, 0, 0, null);
tempCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
//Draw result after performing masking
canvas.drawBitmap(result, 0, 0, new Paint());
}
The mask should be a white image with transparency.
It will work like this:
+ =
I encountered the same problem in my custom view and instead of decoding the bitmap from a resource, I had created the original bitmap and the masking bitmap from the scratch via canvas.draw*() methods (since both the original and mask are basic shapes). I was getting the blank opaque space instead of a transparent one. I fixed it by setting a hardware layer to my view.
View.setLayerType(LAYER_TYPE_HARDWARE, paint);
More info on why this is to be done here: https://stackoverflow.com/a/33483016/4747587
Same answer as #Sergey Pekar give but I have updated it in Kotlin.
fun ImageView.getMaskBitmap(imageUrl: String? = null, mContent: Int, mMaskedImage : Int) {
runOnBackground {
// if you have https image url then use below line
//val original: Bitmap = BitmapFactory.decodeStream(URL(imageUrl).openConnection().getInputStream())
// if you have png or jpg image then use below line
val original: Bitmap = BitmapFactory.decodeResource(resources, mContent)
val mask = BitmapFactory.decodeResource(resources, mMaskedImage) // mMaskedImage Your masking image
val result: Bitmap = Bitmap.createBitmap(mask.width, mask.height, Bitmap.Config.ARGB_8888, true)
val tempCanvas = Canvas(result)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
tempCanvas.apply {
drawBitmap(original, 0f, 0f, null)
drawBitmap(mask, 0f, 0f, paint)
}
paint.xfermode = null
//Draw result after performing masking
runOnBackground(onMainThread = {
this.apply {
setImageBitmap(result)
scaleType = ImageView.ScaleType.FIT_CENTER
}
})
}
}
Github Demo
Bitmap finalMasking = stackMaskingProcess(imageBitmap, bitmapMasking);
private Bitmap stackMaskingProcess(Bitmap _originalBitmap, Bitmap _maskingBitmap) {
try {
if (_originalBitmap != null)
{
int intWidth = _originalBitmap.getWidth();
int intHeight = _originalBitmap.getHeight();
resultMaskBitmap = Bitmap.createBitmap(intWidth, intHeight, Bitmap.Config.ARGB_8888);
getMaskBitmap = Bitmap.createScaledBitmap(_maskingBitmap, intWidth, intHeight, true);
Canvas mCanvas = new Canvas(resultMaskBitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(_originalBitmap, 0, 0, null);
mCanvas.drawBitmap(getMaskBitmap, 0, 0, paint);
paint.setXfermode(null);
paint.setStyle(Paint.Style.STROKE);
}
} catch (OutOfMemoryError o) {
o.printStackTrace();
}
return resultMaskBitmap;
}
I like the approach from Er. Praful Parmar's answer but for me it did not quite work as expected. I had problems, because some scaling was going on without intention.
My Bitmaps had a different density than my device and this messed things up.
Also I wanted to reduce the creation of Objects, so I moved the Paint object to a constant for reuse.
So here is my utils method:
public static final//
Bitmap createWithMask(final Bitmap img, final Bitmap mask) {
final Bitmap result = Bitmap.createBitmap(img.getWidth(), img.getHeight(),
Bitmap.Config.ARGB_8888);
result.setDensity(originalBitmap.getDensity()); // to avoid scaling if density of 'img' is different form the default on your device
final Canvas canvas = new Canvas(result);
canvas.drawBitmap(img, 0, 0, null);
canvas.drawBitmap(mask, 0, 0, PAINT_FOR_MASK);
return result;
}//end-method
private static final Paint PAINT_FOR_MASK = createPaintForMask();
private static final//
Paint createPaintForMask() {
final Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
return paint;
}//end-method
I want to be able to use the following image to mask other bitmap. So basically, I'm trying to get the mask image and replace it's black content with another bitmap. Like this for example:
I'm able to use this mask and create a round version of the original bitmap, but that does not retain that orange border around it. Any thoughts on how I can accomplish this effect? Thanks.
The code that I am using (that only creates a round image) is the following:
private static Bitmap applyMask(ImageView imageView, Bitmap mainImage) {
Canvas canvas = new Canvas();
Bitmap result result = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
canvas.setBitmap(result);
Paint paint = new Paint();
// resize image fills the whole canvas
canvas.drawBitmap(mainImage, null, new Rect(0, 0, 50, 50), paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(sMaskImage, 0, 0, paint);
paint.setXfermode(null);
return result;
}
I use below code snipped to set masked images, this sample "bm" is my bitmap and "mMaskSource" is the resource id of mask object position in my drawable folder.
Bitmap mask = BitmapFactory.decodeResource(getResources(), mMaskSource);
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));
bm = Bitmap.createScaledBitmap(bm, mask.getWidth(), mask.getHeight(),
true);
mCanvas.drawBitmap(bm, 0, 0, null);
mCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
And finally you can use "result" bitmap object however you wish.
I have an ImageView with bitmap in it. This bitmap has alpha channel and transparent pixels.
When i try to use ColorFiter with Mode.OVERLAY (since honeycomb) - provided color overlay the whole imageview (whole rect), but i want only to overlay non transparent pixels. How can i clip the imageview's canvas to perform filter where i want ?
UPDATED
I have grey image in png:
When i try to use MODE_ATOP i get:
When i use OVERLAY i get:
And what i want to get:
There's probably a more efficient way to do this (maybe by creating a ColorMatrixColorFilter to approximate it), but since Mode.OVERLAY appear to be hard to simplify otherwise, here's some sample code that should implement what you want:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ImageView imageView = new ImageView(this);
setContentView(imageView);
final Paint paint = new Paint();
Canvas c;
final Bitmap src = BitmapFactory.decodeResource(getResources(),
android.R.drawable.sym_def_app_icon);
final int overlayColor = Color.RED;
final Bitmap bm1 = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Config.ARGB_8888);
c = new Canvas(bm1);
paint.setColorFilter(new PorterDuffColorFilter(overlayColor, PorterDuff.Mode.OVERLAY));
c.drawBitmap(src, 0, 0, paint);
final Bitmap bm2 = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Config.ARGB_8888);
c = new Canvas(bm2);
paint.setColorFilter(new PorterDuffColorFilter(overlayColor, PorterDuff.Mode.SRC_ATOP));
c.drawBitmap(src, 0, 0, paint);
paint.setColorFilter(null);
paint.setXfermode(new AvoidXfermode(overlayColor, 0, Mode.TARGET));
c.drawBitmap(bm1, 0, 0, paint);
imageView.setImageBitmap(bm2);
}
}
In short, we draw the source bitmap and color using the OVERLAY mode, and then using a secondary bitmap (composited using SRC_ATOP mode), we combine it using AvoidXfermode to not draw over the transparent pixels.
Original image:
Result:
You can use the Overlay mode and then clip out the area that used to be transparent with the same bitmap using DST_ATOP xFerMode. https://developer.android.com/reference/android/graphics/PorterDuff.Mode
private fun applyFilterToImage() {
val bitmapCopy = Bitmap.createBitmap(originalImage.width, originalImage.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmapCopy)
val rnd = java.util.Random()
val randomColor = Color.rgb(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))
val paint = Paint()
val porterDuffMode = PorterDuff.Mode.OVERLAY
paint.colorFilter = PorterDuffColorFilter(randomColor, porterDuffMode)
val maskPaint = Paint()
maskPaint. xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(originalImage, 0f, 0f, paint)
canvas.drawBitmap(originalImage, 0f, 0f, maskPaint) //clips out the background that used to be transparent.
imageView.setImageBitmap(bitmapCopy)
}
MarkerColorChangeGIF