I am trying to invert image mask bitmap using below code
static final PorterDuffXfermode eraseMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
public void invertSelection() {
Bitmap inverted = Bitmap.createBitmap(imageBitmap.getWidth(), imageBitmap.getHeight(), Bitmap.Config.ARGB_8888);
if (!annotationBitmap.sameAs(inverted)) {
Canvas canvas = new Canvas(inverted);
paint.setColor(Color.RED);
canvas.drawPaint(paint);
paint.setXfermode(eraseMode);
canvas.drawBitmap(annotationBitmap, 0,0,paint);
annotationBitmap = inverted;
undoStack.push(annotationBitmap.copy(annotationBitmap.getConfig(), true));
invalidate();
}
}
after calling this function I am no longer able to draw on annotationBitmap.
What am I doing wrong here???
I think you should read this beautiful article at medium
and i am not sure but i think if you changePorterDuff.Mode.CLEAR to PorterDuff.Mode.DST_OUT
your problem will solve
Related
Fresco has built-in support for circular images and rounded corner, but what about other shapes such as diamond or parallelogram, etc?
It's simple to do with the standard ImageView via custom drawable that uses BitmapShader. For example, the following custom Drawable receives the image Bitmap and a slope height to make a an ImageView look like this picture:
public class MaskDrawable extends Drawable {
private Paint mPaint;
private Path mPath;
private int mSlopeHeight;
public MaskDrawable(Bitmap bitmap, int slopeHeight) {
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(shader);
mSlopeHeight = slopeHeight;
mPath = new Path();
}
#Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
mPath.moveTo(0, 0);
mPath.lineTo(0, bounds.bottom);
mPath.lineTo(bounds.right, bounds.bottom - mSlopeHeight);
mPath.lineTo(bounds.right, 0);
canvas.drawPath(mPath, mPaint);
}
To do that with Fresco, I need the Bitmap of the image, but I'm not sure how to do that. I read that I can get the Bitmap directly from the ImagePipeline, but that are many gotchas that comes with it. In one case, the returned Bitmap is short lived and shouldn't be used to draw on the screen where in the other case I get a CloseableReference which I need to release at some point which isn't clear to me. What I have seen on the net so far is code similar to this for getting the Bitmap:
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setRequestPriority(Priority.HIGH)
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
.build();
DataSource<CloseableReference<CloseableBitmap>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, getContext());
DataSubscriber<CloseableReference<CloseableBitmap>> dataSubscriber =
new BaseDataSubscriber<CloseableReference<CloseableBitmap>>() {
#Override
protected void onNewResultImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
mBitmapRef = dataSource.getResult();
// Get the bitmap here and use it in my custom drawable?
}
#Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
}
};
dataSource.subscribe(dataSubscriber, UiThreadImmediateExecutorService.getInstance());
I haven't tried that yet and was wondering if somebody can provide a working solution instead of the bits and bytes I've gathered so far from different places. It has to be done right or else I can easily leak memory, which beats the whole idea of using Fresco from the first place.
You don't need to and also not recommend to use imagepipeline as you're dealing with view.
One way is to manage those bitmap in postprocessor. You need to override the process method, use the same BitmapShader, paint, canvas implementation, use PlatformBitmapFactory createBitmap to create purgeable bitmap CloseableReference, and finally close the reference when you're done with the bitmap.
See more in
http://frescolib.org/docs/modifying-image.html
EDIT
Below is the final implementation I came up with after getting help from Jie Wang. The following code snippet places the image in the shape I presented in the question.
mSimpleDraweeView = (SimpleDraweeView) findViewById(R.id.shaped_picture);
final int slopeHeight = 100;
Postprocessor maskProcessor = new BasePostprocessor() {
#Override
public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
// Get the size of the downloaded bitmap
final int width = sourceBitmap.getWidth();
final int height = sourceBitmap.getHeight();
// Create a new bitmap and use it to draw the shape that we want.
CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(width, height);
try {
Bitmap destBitmap = bitmapRef.get();
// Create canvas using the new bitmap we created earlier
Canvas canvas = new Canvas(destBitmap);
// Set up the Paint we will use for filling in the shape
// BitmapShader will fill the shape with the downloaded bitmap
BitmapShader shader = new BitmapShader(sourceBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
// Set up the actual shape. Modify this part with any shape you want to have.
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(0, height);
path.lineTo(width, height - slopeHeight);
path.lineTo(width, 0);
// Draw the shape and fill it with the paint
canvas.drawPath(path, paint);
return CloseableReference.cloneOrNull(bitmapRef);
}
finally {
CloseableReference.closeSafely(bitmapRef);
}
}
};
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setPostprocessor(maskProcessor)
.build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setOldController(mSimpleDraweeView.getController())
.build();
mSimpleDraweeView.setController(controller);
I am trying to figure out how to simply draw a line on an image that is being set in Picasso. I found that if I simply set the image, given a URI, with Picasso and try to draw paint to it using the following:
canvas = new Canvas(bitmap);
image.draw(canvas);
topEdge = new Paint();
topEdge.setColor(context.getResources().getColor(R.color.blue));
topEdge.setStrokeWidth(5);
canvas.drawLine(c1.getX(), c1.getY(), c2.getX(), c2.getY(), topEdge);
Then I get a crash saying that the bitmap needs to be mutable first. So I added this above that code:
Bitmap workingBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
And then create the canvas with new Canvas(mutableBitmap) instead. This removed the crash, however nothing is being drawn. I believe this is because my Picasso is setting the image before, so now I need to reset Picasso with this new mutable bitmap. The problem is this code is in the onSuccess() callback for Picasso. What can I do to allow Paint to be drawn on an image through Picasso?
just follow the steps below:
Write your own class extends the class Transformation like below:
class DrawLineTransformation implements Transformation {
#Override
public String key() {
// TODO Auto-generated method stub
return "drawline";
}
#Override
public Bitmap transform(Bitmap bitmap) {
// TODO Auto-generated method stub
synchronized (DrawLineTransformation.class) {
if(bitmap == null) {
return null;
}
Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), true);
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
canvas.drawLine(0, resultBitmap.getHeight()/2, resultBitmap.getWidth(), resultBitmap.getHeight()/2, paint);
bitmap.recycle();
return resultBitmap;
}
}
}
2、Add the Transformation to RequestCreator created with Picasso.load() function like below:
Picasso picasso = Picasso.with(getApplicationContext());
DrawLineTransformation myTransformation = new DrawLineTransformation();
picasso.load("http://www.baidu.com/img/bdlogo.png").transform(myTransformation).into(imageview);
That's all steps you need to do , just enjoy!
I have a Picture object, loaded from an SVG file, and I have set hardwareAccelerated=false to make it works on all devices.
Since there is a bug on android 4.0.4, I have to convert the Picture to Bitmap and I do that, in this way:
...
...
public void draw(Canvas canvas) {
...
...
//myPicture size is 9000x5000 but I want to display only this portion
clipRect.set(50, 50, 370, 530);
Bitmap bmp = getBitmapFromPicture(myPicture, clipRect);
canvas.drawBitmap(bmp, 0, 0, null);
bmp.recycle();
...
...
}
public static Bitmap getBitmapFromPicture(Picture picture, RectF clipRect) {
Bitmap bitmap = Bitmap.createBitmap(Math.round(clipRect.width()), Math.round(clipRect.height()), Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(picture);
}
Now I want to clip the Picture because I want to display only the visible screen part of it.
But the canvas.drawPicture does not accept srcRect parameter.
How is it possible to achieve this?
EDIT:
By translate the canvas: canvas.translate(-50, -50) it seems that translate the bitmap, too.
You need to set a transform on the Canvas.
To move the portion of the picture at 50,50 down so it is on the bitmap (ie. at 0,0), just do:
canvas.translate(-50, -50);
So your method becomes:
public static Bitmap getBitmapFromPicture(Picture picture, RectF clipRect)
{
Bitmap bitmap = Bitmap.createBitmap(Math.round(clipRect.width()),
Math.round(clipRect.height()),
Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.translate(-clipRect.left, -clipRect.top);
canvas.drawPicture(picture);
}
So I tried the code from here: Creating an ImageView with a mask. I'm using the following images as original and mask:
However, the result I get is this:
Note that the window background is not black, but holo light (which on the galaxy nexus looks like a very pale gray, not completely white). The second image is the result I get when an item is selected on a list view.
If instead I create a new Bitmap using the same algorithm and then pass it to the image view instead of overriding onDraw(), it draws correctly:
Canvas canvas = new Canvas();
Bitmap mainImage = //get original image
Bitmap maskImage = //get mask image
Bitmap result = Bitmap.createBitmap(mainImage.getWidth(), mainImage.getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(result);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.drawBitmap(mainImage, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(maskImage, 0, 0, paint);
paint.setXfermode(null);
imageView.setImageBitmap(result);
I get the expected result:
Note the fade is correctly applied. This is more evident when a selection is made.
So what's going on on ImageView's onDraw method to create this black backdrop instead of letting the window background show through? What's interesting is that if the original image itself has some transparency, that transparency is respected, for example:
I can't figure it out by myself. I'd rather be able to do it on onDraw instead of pre-creating the bitmap because it only works for bitmaps as source and mask. I want to be able to do it with other drawables like gradients and solid colours but on those cases the width and height are not set.
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();
}
Answering my own question. The Xfermode was working as intended. The paint was making the resulting are of the canvas transparent (which was the canvas used by the window activity). Since the canvas itself was being set transparent, the window was showing what was behind it: the black background.
To do it properly, indeed a new Bitmap has to be created to hold the result of the alpha mask. I updated the code to take into account drawables of all types.
In this Code Apply:
mask_over = BitmapFactory.decodeResource(
getResources(), mask_over1[0]);
icon = Bitmap.createScaledBitmap(icon, screenwidth, screenwidth, false);
mask_over = Bitmap.createScaledBitmap(mask_over, screenwidth, screenwidth, false);
back_img=createBitmap_ScriptIntrinsicBlur(Bitmap.createScaledBitmap(cropview.croppedImage, screenwidth, screenwidth, false),25.0f);
LinearLayout.LayoutParams layoutParams111 = new LinearLayout.LayoutParams(screenwidth, screenwidth);
I'm trying to do something that I thought was simple but apparently it's not!
I'm making a simple app to learn Android game dev. At the moment, the user clicks on the screen and a sprite is displayed. The sprite is just a solid white square. I want to change that in the code.
I think I'm spoiled by C#/XNA where, if a sprite is solid white, you can set the colour in the draw method. That doesn't seem to work here, even when I used the paint function. I've done a bit of research but all the solutions don't seem to be quite what I'm looking for, and also very complicated. I'm hoping to find something at least simpler to mess with if I can't find the exact solution. Here's the relevant code so far:
From the view:
private Sprite createSprite(int resouce) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
return new Sprite(this, bmp, touchX, touchY);
the view onDraw method:
if (touched == true)
{
canvas.drawColor(colors[nextColor]);
lastColor = colors[nextColor];
//add bitmap to array
sprites.add(createSprite(R.drawable.test));
//draw sprites
for (Sprite sprite : sprites) {
sprite.onDraw(canvas);
}
relevant code from the sprite class:
private Paint paint = new Paint();
public Sprite(DaphnyView daphView, Bitmap bmp, int positionX, int positionY) {
width = bmp.getWidth();
height = bmp.getHeight();
DaphView = daphView;
Bmp = bmp;
PositionX = positionX;
PositionY= positionY;
paint.setColor(Color.GREEN);
}
public void onDraw(Canvas canvas) {
canvas.drawBitmap(Bmp, PositionX, PositionY, paint);
}
If anyone can help me out or at least point me to a good starting point, it would be a great help. Thanks!
I found a solution by creating a new canvas to modify the bitmap, then passing this modified bitmap back to the original canvas.