I followed some other questions on the topic, and also API demos on XferModes, and attempted to create an ImageView that masks its content according to an alpha mask.
The alpha bitmap contains 0 alpha for areas to be hidden and 255 alpha for areas to be shown.
This is the onDraw code:
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawBitmap(mMaskBitmap, 0, 0, paint);
paint.setXfermode(null);
It doesn't work for me. If I put SRC_IN, the mask just gets drawn on top. If I put DST IN, I just see the original image.
Any advice?
The problem was solved, I discovered that the ImageView and the mask bitmap's size had a difference of 4 pixels, which caused the whole thing to silently malfunction... Weird that there was no exception thrown.
Anyway, the above code works, provided that the mask size <= the ImageView size.
Related
I'm attempting to draw a path, and then mask it with another path using the DST_IN Porterduff Mode.
Here's my block of code that does the drawing
int saveCount = canvas.saveLayer(
0, 0, canvas.getWidth(), canvas.getHeight(), null);
canvas.drawPath(mPath, mFillPaint);
mClipPaint = new Paint();
mClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawPath(mClipPath, mClipPaint);
This does the masking but also has a rectangle around the path beyond which the masking doesn't work as seen below. The outer star is the original path and the blob is the masking path. You can see that the blob has a rectangular border beyond which masking doesn't work
As a side note, this only seems to work if hardware acceleration is enabled. Is this expected?
I am trying to create a Black screen with a transparent Hole in the middle of the screen. Here is what i have tried.
#Override
public void draw(Canvas canvas)
{
Paint myPaint = new Paint();
myPaint.setColor(0xC0000000);
canvas.drawRect(mBlackRect, myPaint);
myPaint = new Paint();
myPaint.setColor(Color.TRANSPARENT);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(mTransparentRect, myPaint);
}
The second paint, shows black color instead of transparent. How can i punch a transparent hole in MY SemiBlack Canvas?
you didn't save the canvas, try the code below
Paint myPaint = new Paint();
int sc = canvas.saveLayer(mBlackRect.left, mBlackRect.top,
mBlackRect.right, mBlackRect.bottom, myPaint,
Canvas.ALL_SAVE_FLAG);
myPaint.setColor(0xC0000000);
canvas.drawRect(mBlackRect, myPaint);
myPaint.setColor(Color.TRANSPARENT);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(mTransparentRect, myPaint);
myPaint.setXfermode(null);
canvas.restoreToCount(sc);
You can not really "punch" a hole by "removing pixels" from something already drawn, at least not with a hardware layer. And if you use a software layer, it will be bad for performance.
What you want to do is draw your shape with an alpha mask applied to your paint. A mask will prevent some parts of the shape to be drawn on the canvas, like cutting a piece of paper and stick it on a wall before spreading the painting.
To apply an alpha mask to your paint, you first need to create a bitmap containing the "hole" shape (programmatically or by loading a custom image from resources), then create a BitmapShader from this bitmap with the proper Xfermode (depending if you want the transparent part in your mask bitmap to be cut out or the non-transparent part) and finally apply this shader to your paint before drawing the semitransparent rectangle or anything you want.
Be careful with performance: only create the Paint object once (do not allocate any object in onDraw() because this method gets called up to 60 times per second on the UI thread), and recreate the alpha mask bitmap only when the bounds of your View/Drawable change (if its dimensions depend on the View dimensions of course, otherwise you just create it once).
I'm sorry if I don't have time to give you ready-to-use code but I think you should find plenty of information about the technique I just described and you can start experimenting and figuring out the solution by yourself which is more rewarding I think ;)
So this is what I have for a vignette style effect in Android (image is a Bitmap):
public void vignette() {
float radius = (float) (image.getWidth()/1.5);
RadialGradient gradient = new RadialGradient(image.getWidth()/2, image.getHeight()/2, radius, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
Canvas canvas = new Canvas(image);
canvas.drawARGB(1, 0, 0, 0);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setShader(gradient);
final Rect rect = new Rect(0, 0, image.getWidth(), image.getHeight());
final RectF rectf = new RectF(rect);
canvas.drawRect(rectf, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(image, rect, rect, paint);
}
This "works" but there are a couple of problems. First of all, this is not really a vignette, it's just a gradient so you can see bits of the black going nearly all the way to the center rather than feathering out closer to the edges.
The RadialGradient used also only allows for setting the radius of a circle rather than an ellipse. An ellipse would be able to more effectively match the dimensions of a non-square image than a circle.
The quality of the gradient is also not superb.
I'm trying to replicate the vignetteImage method from ImageMagick (I'm referring specifically to the php version). I have this code in PHP that produces the style of image that I want:
$im = new IMagick('city.png');
$im->vignetteImage($width/1.5, 350, 20, 20);
I've tried building ImageMagick with the NDK but have been unsuccessful in properly linking the various image libraries (I've only successfully built with gif support but no png, jpeg or tiff).
I've also attached an image comparing the two methods shown above. The image on the left was generated with ImageMagick through php and the image on the right was generated using the method shown above for Android.
If you look carefully at the image on left, tf uses exponential increase in Alpha (transparency) vs. image on right which is very linear.
Clearly Shader.TitleMode.CLAMP is a linear function. What you should do instead is use RadialGradient(float x, float y, float radius, int[] colors, float[] positions, Shader.TileMode tile) to define 10 or more points on the image with exponentially decreasing color values (black to transparent).
Alternatively, you can refer gallery 3d source for ICS gallery http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.0.1_r1/com/android/gallery3d/photoeditor/filters/VignetteFilter.java?av=h
I know this is an old discussion but it may help someone.
You can use AccelerateInterpolator to generate the points that Taranfx had mentioned and it will lock awesome.
I'm developing an android app where i need to capture text and save it as a transparent image. Capturing the text has been done but making a transparent png file is where i'm stuck as i'm not familiar with image pixel manipulation at all. Here's what I have so far... i first create a blank bitmap and fill it with a white background, then i set the paint's transparency to 0 (full transparency) and then draw the source bitmap into the destination bitmap using the XOR modes.. but when i run the app all i see is a blank white image. i'll be glad if someone points out what i'm doing wrong and how to fix it. Thanks in advance.
b = Bitmap.createBitmap(tw, th,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
Rect dest = new Rect(0,0,b.getWidth(),b.getHeight());
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, b.getWidth(), b.getHeight(), paint);
paint.setAlpha(0);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawBitmap(bmp,null,dest,paint);
Have you looked at : How to change a bitmap's opacity?
Seems like
paint.setAlpha(0);
won't do anything as you need to set the alpha channel to something greater than 0...
Use:
Color.argb(0,0,0,0)
The first parameter is the alpha. Set it to 0 for complete transparency.
I have a custom view with the following drawing code:
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
mPaint.setColor(Color.GREEN);
canvas.drawRect(0, 0, 100, 100, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(100, 100, 100, mPaint);
}
This should draw a square, and then a circle that is twice the size of the square, centred on one of its corners. Instead the circle is the same size as the square.
In other words. It should look like the image on the left, but it actually looks like the image on the right. What gives?
Ok, after testing, this appears to be a bug in the GUI layout editor. I was assuming this would match the results on the device since they actually run the code... and it's a very weird bug that it only affects circles!
Also it is a more complicated bug than just doubling the values. Sometimes drawCircle() draws an ellipse!
Anyway it works as expected on the device.